1. XSS

(1) 이해

XSS(Cross-Site Scripting)는 웹 애플리케이션의 보안 취약점 중 하나로, 악의적인 사용자가 웹 페이지에 스크립트를 삽입하여 다른 사용자의 브라우저에서 실행시키는 공격 기법이다. 마치 다른 사람의 집에 몰래 들어가 전등을 켜거나 음악을 틀어놓는 것과 비슷하다.
[그림 1-1] XSS 공격[그림 1-1] XSS 공격
[그림 1-1] XSS 공격
위 그림과 같이 공격자는 XSS를 사용하여 사용자에게 악성 스크립트를 보내고, 사용자의 브라우저는 스크립트가 신뢰할 수 있는 출처에서 왔다고 간주하므로 해당 사용자의 브라우저에 보관되고 실행된다. 그 결과, 악성 스크립트는 사용자의 브라우저에서 사용되는 모든 쿠키, 세션 토큰 또는 기타 민감한 정보에 액세스할 수 있게 된다. 이렇게 공격자는 XSS를 통해 사용자의 정보를 탈취하거나 웹 사이트를 변조/손상시킬 수 있다.
 

(2) XSS 공격의 종류

1) Stored XSS(저장형 XSS)

Stored XSS악의적인 스크립트가 서버에 저장되어 다른 사용자가 해당 웹 페이지에 접속할 때마다 실행되는 XSS 공격이다. 데이터베이스에 스크립트가 직접 저장되는 방식으로 공격이 이루어지므로 다른 사용자들에게 지속적으로 위협이 된다. Stored XSS는 댓글이나 게시글, 프로필 등 사용자 생성 콘텐츠가 저장되는 곳에서 주로 발생한다.
예를 들어, 게시판에 아래와 같은 코드를 포함하여 글을 작성했다고 가정해보자.
<script>alert('XSS 공격!');</script>
따로 XSS 공격에 대한 예방이 되어있지 않다면 서버에 저장된 해당 게시글에 다른 사용자가 접속할 때마다 스크립트가 실행되어 경고창이 뜨게 될 것이다.

2) Reflected XSS(반사형 XSS)

Reflected XSS 사용자가 입력한 값이 서버에서 처리된 후 다시 클라이언트에게 반환될 때 발생하는 XSS 공격이다. 즉, 사용자가 악성 스크립트를 포함한 입력을 서버에 보냈을 때 서버가 이를 필터링하지 않고 그대로 반환하여 공격이 실행되는 것이다. 주로 URL에 악성 스크립트를 포함해 보내는 식으로 공격이 이루어지며, 일회성 공격이라는 특징이 있다. 이와 같이 Reflected XSS는 검색 엔진, 로그인 페이지, 게시판의 검색 기능 등에서 주로 발생한다.
예를 들어, http://example.com/search?q=<script>alert('XSS 공격!');</script> 과 같이 URL을 입력하여 검색을 수행하게 되면 검색 결과 페이지에서 해당 스크립트가 실행되어 경고창이 뜨게 될 것이다.

3) DOM-based XSS(DOM 기반 XSS)

DOM-based XSS클라이언트 측 스크립트에서 DOM을 조작하여 발생하는 XSS 공격이다. 클라이언트 측 JavaScript 라이브러리의 취약점을 이용하여 공격하는 경우가 많다. 이렇듯 서버 측이 아닌 클라이언트 측에서 취약점이 발생하기 때문에 서버 측 필터링 만으로는 방어가 어려우므로, 사용자 입력을 그대로 DOM에 추가하는 경우 특히 유의해야 한다. DOM-based XSS는 JavaScript로 동적으로 생성되는 HTML 콘텐츠 등에서 주로 발생한다.
예를 들어, 아래와 같이 사용자의 입력 값을 읽어와 그대로 DOM에 추가하는 JavaScript 코드가 있다고 가정해보자.
<input type="text" id="userInput"> <button onclick="displayMessage()">메시지 표시</button> <div id="message"></div> <script> function displayMessage() { // 사용자 입력 값 가져오기 const userInput = document.getElementById("userInput").value; // 사용자 입력 값을 직접 innerHTML에 삽입 (취약점!) document.getElementById("message").innerHTML = userInput; } </script>
위의 경우 입력 값에 <script>alert('XSS 공격!');</script> 과 같은 값이 들어오게 되면, JavaScript 코드에서 해당 입력 값을 읽어와 DOM에 직접 추가하게 되므로, 스크립트가 실행되어 경고 창이 뜨게 된다.
 
세 가지의 XSS 공격을 요약해보면 다음 표와 같다.
구분
Stored XSS
Reflected XSS
DOM-based XSS
특징
저장된 데이터를 통해 공격
사용자 입력이 서버를 거쳐 다시 클라이언트로 반사되어 공격
클라이언트 측 스크립트에서 DOM을 조작하여 공격
발생 위치
서버
서버와 클라이언트 사이
클라이언트
저장 위치
서버에 저장됨
서버에 저장되지 않음
서버에 저장되지 않음
공격 지속성
지속적
일회성
일회성 또는 지속적
공격 범위
다수 사용자
특정 사용자
특정 사용자
위 예시들은 설명을 돕기 위해 가정한 간단한 XSS 공격의 예시이며, 실제 XSS 공격은 더욱 복잡하고 다양한 형태로 이루어질 수 있다.
 

(3) XSS 발생 경우

1) 입력 값에 대한 검증 부족

입력 값에 대한 검증이 부족할 때 XSS 공격에 노출될 수 있다. 웹 어플리케이션에서 사용자 입력을 제대로 검사하지 않고 그대로 반영할 경우, 악의적인 스크립트가 삽입될 수 있다.

2) 출력 값에 대한 인코딩 부족

출력 값에 대한 인코딩이 부족할 때에도 이러한 XSS 공격에 노출될 수 있다. 웹 페이지에 출력되는 데이터를 적절하게 인코딩하지 않으면, 브라우저가 스크립트로 해석하여 실행하게 된다.
그러므로, XSS 공격을 예방하기 위해서는 입력 값에 대한 검증과 출력 값에 대한 인코딩이 필수적으로 이루어져야한다.
 

(4) XSS 대책 방법

1) 입력 값 검증

먼저 사용자가 입력한 값에 대해 검증을 거치는 방법이 있다. 이는 사용자가 입력한 데이터에 악의적인 코드가 포함되어 있는지 검사하는 과정이다. 입력 값을 검증하는 방법에도 여러가지가 있다. 이러한 입력 값 검증은 XSS 공격의 첫 번째 방어선이며, 가장 중요한 예방 조치 중 하나이다.
입력값 검증 방법
입력값 검증 방법
  • 화이트리스트 방식: 허용되는 문자와 특수 문자만 정의하고, 이를 제외한 모든 문자는 거부하는 방법
  • 블랙리스트 방식: 금지된 문자와 특수 문자를 정의하고, 이러한 문자가 포함된 입력은 거부하는 방법
  • 정규 표현식: 복잡한 패턴을 사용하여 악의적인 코드를 탐지하고 제거하는 방법
  • 입력 길이 제한: 입력 가능한 문자의 최대 길이를 제한하여 과도한 입력을 방지하는 방법

2) 출력 값 인코딩

출력 값 인코딩은 웹 페이지에 출력되는 데이터를 HTML 엔티티로 변환하여 브라우저가 스크립트로 해석하지 못하도록 하는 과정이다. 출력 값을 인코딩하는 방법은 아래와 같다. 출력 값 인코딩은 입력 값 검증과 함께 사용하여 더욱 강력한 보안 체계를 구축할 수 있다.
출력 값 인코딩 방법
출력 값 인코딩 방법
  • HTML 엔티티: <, >, &, ', " 등의 특수 문자를 &lt;, &gt;, &amp;, &apos;, &quot;와 같은 HTML 엔티티로 변환
    • <script>alert('XSS 공격!');</script> ➡️&lt;script&gt;alert('XSS Attack!')&lt;/script&gt;
  • Context-aware escaping: 출력되는 위치에 따라 적절한 인코딩 방식을 적용
    • (예를 들어, JavaScript 코드 내에서 출력되는 데이터는 JavaScript 엔티티로 인코딩)
 
XSS 공격을 예방하기 위해서는 입력 값 검증, 출력 값 인코딩 등 다양한 보안 조치를 함께 적용해야 한다. 또한, 개발 과정에서부터 보안을 고려하고, 정기적인 보안 점검을 수행하는 것이 중요하다.