소프트웨어 개발보안 가이드 분석(2021) : 크로스사이트 스크립트(XSS)

크로스사이트 스크립트(XSS)

https://owasp.org/www-community/attacks/xss/

크로스사이트 스크립트(XSS)의 경우 웹 애플리케이션에서 가장 흔하고 치명적인 보안 약점 중 하나입니다. XSS는 웹 애플리케이션에서 사용자 입력값에 대한 필터링이 제대로 이루어지지 않을 경우, 공격자가 입력 가능한 폼에 악의적인 스크립트를 삽입하여 해당 스크립트가 희생자 측에서 동작하도록 하는 보안 약점입니다.

XSS 공격 종류

1. Stored XSS(저장형 XSS)

  • 공격자가 악의적인 스크립트를 웹 애플리케이션의 데이터베이스에 저장하고, 이를 불러들여 스크립트가 작동하는 방식입니다.
  • 데이터베이스에 저장되기 때문에 해당 스크립트가 URL에 접속하게 되면 모든 사용자에게 영향을 미칠 수 있습니다.
  • 주로 게시글, 댓글, 프로필 등에 악성 스크립트가 삽입됩니다.

2. Reflected XSS(반사형 XSS)

  • 공격자는 악성 링크를 생성하거나, 사용자의 입력을 이용하여 악성 스크립트를 서버에 전달합니다.
  • 이때, 서버에서는 스크립트가 저장되지 않고 사용자에게 반환되어 실행됩니다.
  • 주로 공격자는 사용자에게 악성 링크를 전달하거나, 피해자가 악성 링크를 클릭하도록 유도합니다.

3. DOM-based XSS(DOM형 XSS)

  • 공격자는 악성 스크립트를 포함한 URL을 사용자에게 전달합니다.
  • 사용자가 해당 URL을 방문하면, 브라우저는 URL에 포함된 악성 스크립트를 실행합니다.
  • 이때, 스크립트는 서버로부터의 응답이 아닌 클라이언트 측에서 실행되므로, 서버에는 악성 스크립트가 전달되지 않습니다.

 

  • Stored XSS에 취약한 코드
1	String UserInput = request.getParameter(“comment”);
2	String query = “INSERT INTO comments (comment) VALUES(‘ “ + UserInput + “ ‘)”;
3	Statement.executeUpdate(query);
  • 1행 : 사용자로부터 입력을 받아들입니다.(comment : 사용자 입력 폼의 파라미터)
  • 2, 3행 : 사용자의 입력을 데이터베이스에 그대로 저장합니다.
  • 사용자가 입력한 내용이 그대로 데이터베이스에 저장되기 때문에, 악의적인 스크립트를 입력할 경우 이를 데이터베이스에 저장하게 됩니다.
  • 저장된 스크립트는 나중에 다른 사용자가 해당 페이지를 방문할 때 불러와지며, 이때 악의적인 스크립트가 실행되어 해당 사용자의 브라우저를 공격할 수 있습니다.
  • Reflected XSS에 취약한 코드
1	String UserInput = request.getParameter(“search”)

2	PrintWriter out = response.getWriter();
3	out.pinrtln(“<html>”);
4	out.pinrtln(“<body>”);
5	out.pinrtln(“<h2>” +UserInput +”</h2>”);
6	out.pinrtln(“</html>”);
7	out.pinrtln(“</body>”);
  • 1행 : 사용자로부터 입력을 받아들입니다.(search : 사용자 입력 폼의 파라미터)
  • 2~7행 : 사용자의 입력이 HTML 페이지에 출력됩니다.
  • 사용자로부터 입력을 받아들여 화면에 반영합니다. 즉, 스크립트가 서버에 저장되지 않고 그대로 반환되기 때문에 Reflected라고 명칭합니다.
  • 사용자가 입력한 내용이 그대로 화면에 출력되기 때문에, 사용자가 악의적인 스크립트를 입력할 경우 해당 스크립트가 그대로 화면에 반영되어 실행될 수 있습니다.

  • Dom-based XSS에 취약한 코드
1	<!DOCTYPE html>
2	<html lang=”en”>
3	<head>
4	   <script>
5	     Function displayMessage() {
6	        var UserInput = document.getElementById(“message”).value;
7	        Document.getElementById(“output”).innerHTML = UserInput;
8	     }
9	   </script>
10	</head>
11	<body>
12	   <input type=”text”, id=”message”>
13	   <button onclick=”displayMessage()”>전송</button>
14	   <div id=”output”></div>
  • 4~9행 : <script> 태그 내의 displayMessage() 함수는 사용자가 입력한 메시지를 가져와서 화면에 표시합니다.
  • message : 입력 폼 id
  • output : 사용자가 입력한 값을 화면에 표시하기 위한 HTML 요소
  • 스크립트가 서버 측에서 발생하는 것이 아니라 클라이언트 측 JavaScript에서 발생합니다.
  •  

XSS 대응 방법

  • 입력 데이터의 필터링 및 검증
    • 사용자 입력값에 대한 검증은 반드시 서버단에서 이루어져야 합니다. 만일 검증을 클라이언트단에서 한다면, 웹 프록시 툴을 이용해 손쉽게 우회가 가능하기 때문입니다.
  • 이스케이프 처리
    • 사용자 입력값을 이스케이프 하면 HTML 특수 문자를 HTML 엔티티로 변환하여 해당 문자가 HTML 특수문자로 해석되지 않습니다. 이로 인해 스크립트가 입력되어도 실행되지 않고 일반 텍스트로 출력됩니다.

HTML 특수문자

HTML Entity

< , >

&lt;, &gt;

&

&amp;

" , '

&quot;, &#039;

안전한 코드 사용

Ⅰ<c:out> 태그 처리를 통한 이스케이프 처리

1	<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
2	<!DOCTUPE html>
3	...
4	<body>
5       <p>User Input: <c:out value="${userInput}" /></p>
6	</body>
7	...

1행 : JSP 표준 태그 라이브러리를 호출하는 역할을 합니다.

5행 : 사용자 입력값을 ${userInput}으로 두고 이 값이 출력할 때는 <c:out> 태그를 이용하여 HTML 특수문자가 입력되면 HTML Entity로 처리합니다.

즉, <c:out> 태그를 사용하면 악의적인 스크립트가 실행되지 않고 그대로 출력되어 XSS 공격에 방지할 수 있습니다.

Ⅱ JAVA 코드를 통한 이스케이프 처리

1	publid static String escapehtml(String input) {
2	   if (input == null) { return null; }
3	   StringBuilder escaped = new StringBuilder();
4	      for (char c : input.toCharArray()) {
5	         switch(c) {
6	            case '<' -> escaped.append("&lt;");
7	            case '>' -> escaped.append("&gt;");
8	            case '&' -> escaped.append("&amp;");
9	            case '"' -> escaped.append("&quot;");
10	            case '\' -> escaped.append("&#39;");
11	            default -> escaped.append(c);
12           }
13        }
14        return escaped.toString();
15     }

1행 : escapehtml 메서드는 입력받은 문자열을 HTML Entity로 변환하여 반환하는 메서드를 구현하는 것입니다.

3행 : HTML Entity로 변환된 문자열을 저장하기 위해서 StringBuilder 객체를 생성합니다.

6~10행 : switch 문을 이용해 HTML 특수문자가 입력되면 그에 대응하는 문자로 이스케이프 처리를 합니다.

11행 : HTML 특수문자에 해당되지 않는다면 입력받은 문자 그대로 처리합니다.

14행 : 안전하게 변환된 HTML Entity 문자열을 반환합니다.

외부 라이브러리를 사용하지 않고 JAVA 코드 내에서 메서드를 직접 구현하여 이스케이프 처리하여 XSS 공격에 방지할 수 있습니다.

Ⅲ HTML Tag Filter

위의 Ⅰ, Ⅱ의 방법을 사용해도 XSS 공격에 방지할 수 있습니다. 하지만 이렇게 치환하는 것은 사용자 입력이 적은 웹의 경우 가능하겠지만 무수히 많은 곳에서 사용자 입력을 받는다면 모든 곳에서 일일이 이스케이프 처리를 할 수 있는 코드를 작성해 줘야 합니다.

하지만 HTML Tag Filter를 사용한다면 한 곳(web.xml)에서 이스케이프 처리를 할 수 있도록 설정할 수 있습니다.

  • web.xml
    • web.xml 파일에서는 HTML 태그 필터를 등록하고, 모든 URL(7행)에 대해 HTMLTagFilter를 적용합니다.
  • HTMLTagFilter 클래스
    • HTMLTagFilter는 TagFilter와 RequestWrapper 구분하여 서비스 내로 들어오는 모든 요청에 대한 유효성 검증과 이스케이프 처리를 담당합니다.

이렇게 설정된 필터는 모든 HTTP 요청에 대해 파라미터 값을 받아와 HTML 태그를 제거한 후 안전한 값으로 전달합니다. 따라서 사용자 입력에 포함된 악성 스크립트가 실행되는 것을 방어하여 XSS 공격을 막을 수 있습니다.

  • 이전 소프트웨어 개발보안 가이드 분석(2021) : 경로 조작 및 자원 삽입
  • 다음 소프트웨어 개발보안 가이드 분석(2021) : 운영체제 명령어 삽입