마이의 개발 블로그

[Web] 크로스 사이트 스크립팅(XSS) 방어하기 본문

개발지식/Web

[Web] 크로스 사이트 스크립팅(XSS) 방어하기

개발자마이 2024. 6. 10. 15:27
반응형

배경

솔루션 개발 도중 XSS 취약점이 발견되어 이를 어떤 방식으로 해결할지 탐색하고 팀과 의견을 나누는 시간이 필요했습니다. XSS에는 여러 가지 방식이 있지만 특히 웹에서 텍스트 에디터를 통해 사용자가 게시글 본문을 입력하는 등 일반 유저가 HTML을 조작할 수 있는 경우에 해당 문제가 종종 보고되는 것을 발견할 수 있습니다. 보안유지를 위해 더 자세하게 기술할 수는 없지만 저의 경우도 비슷한 맥락의 문제가 발견되어 이를 해결하는 과정에서 알게된 내용들을 작성해보고자 합니다.

크로스 사이트 스크립팅(Cross Site Scripting, XSS)이란?

XSS는 관리자가 아닌 유저가 클라이언트측에 악의적인 스크립트를 삽입하여 이상 동작을 유발시키는 행위로 웹 어플리케이션의 서버 - 클라이언트 구조에서 주로 발생합니다. 이는 보통 아래의 3가지 방식으로 발생시킬 수 있습니다:

 

1. 저장형 XSS (Stored XSS)

- 서버에 스크립트를 저장하여 특정 페이지가 열릴 때마다 스크립트가 실행됨(게시글이나 프로필 방문 시 실행)

2. 반사형 XSS (Reflected XSS)

- 유저가 입력한 스크립트가 서버로부터 응답되어 브라우저에서 실행됨(검색어 입력시 검색결과 페이지에서 실행)

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

- 클라이언트에서 DOM에 변화를 주어 스크립트가 실행됨(웹 에디터 등) 

(1과 2는 서버를 거치며, 3은 클라이언트로만 동작이 가능합니다)

크로스 사이트 스크립팅(XSS) 방어하기

어떤 방식이든지간에 의도하지 않은 동작을 스크립트를 통해 유발한다는 공통점을 갖는데, 이는 스크립트를 실행시키는 특수문자에 대한 처리를 통해 방지할 수 있습니다. 아래와 같은 특수문자 처리 방식들이 있습니다.

 

1. 정규표현식을 활용한 특수문자 입력 제한

function validateInput(input) {
    const regex = /^[a-zA-Z0-9]*$/; // 알파벳과 숫자만 허용
    return regex.test(input);
}

2. 특수문자 인코딩 후 서버에서 디코딩

function encodeHTML(str) {
    return str.replace(/&/g, '&')
              .replace(/</g, '&lt;')
              .replace(/>/g, '&gt;')
              .replace(/"/g, '&quot;')
              .replace(/'/g, '&#39;');
}
import java.util.HashMap;
import java.util.Map;

public class HtmlDecoder {

    private static final Map<String, String> HTML_ENTITIES = new HashMap<>();

    static {
        HTML_ENTITIES.put("&amp;", "&");
        HTML_ENTITIES.put("&lt;", "<");
        HTML_ENTITIES.put("&gt;", ">");
        HTML_ENTITIES.put("&quot;", "\"");
        HTML_ENTITIES.put("&#39;", "'");
    }

    public static String decodeHTML(String str) {
        if (str == null) {
            return null;
        }

        for (Map.Entry<String, String> entry : HTML_ENTITIES.entrySet()) {
            str = str.replace(entry.getKey(), entry.getValue());
        }

        return str;
    }
}

3. 각종 라이브러리를 이용한 직접 출력 막기: JSTL의 <c:out> 등

 

저는 여기서 개발팀과의 논의 끝에 2번 방식(인코딩 후 디코딩)을 활용하여 문제를 해결했습니다. 솔루션 특성상 특수문자의 입력이 그대로 화면에 노출되어야하는 상황이었기 때문입니다. 개발 환경과 주어진 문제 상황에 따라 위 방법들을 유연하게 적용하면 되겠습니다.

반응형
Comments