0. XSS란?
XSS는 Cross-Site Scripting의 약자이다.
XSS 공격은…
- 가장 널리 알려진 웹 보안 취약점 중 하나.
- 사이트에 악성 스크립트를 삽입하며 악의적으로 공격하는 것.
- XSS를 통해 C&C(좀비 PC에 명령을 내리거나 악성 코드를 제어하는 서버)로 리다이렉트하거나, 사용자의 쿠키를 탈취하여 세션 하이재킹 공격을 할 수 있다.
- 대표적 공격 방식은 아래 세 가지가 있다.
- Stored XSS
- Reflected XSS
- DOM Based XSS
1. 대표적 XSS 공격 방식
(1) Stored XSS
가장 흔히 예시로 소개되는 방법이다.
공격자가 서버에 어떤 스크립트를 저장한다.
→ 이후 서비스를 제공하는 정상 페이지에서 다른 사용자들에게 이 스크립트가 노출된다.
예를 들면,
생성을 요청하는 어떤 POST 방식의 API를 호출하는 form 태그에 <script>alert("XSS ATTACK!!!")</script>를 입력하고 엔터를 친다. 정상 생성이 되면, 모든 사용자는 이 서버에서 해당 생성된 데이터를 조회할 때마다 공격 스크립트가 실행이 되고, 위 스크립트의 경우에는 알럿 창이 뜨게 된다.
(2) Reflected XSS
Reflected XSS는 Stored XSS와는 달리 스크립트를 서버에 저장하지 않고, 응답 페이지로 바로 클라이언트에 전달된다는 차이점이 있다.
이는 “검색어”와 같은 쿼리 스트링을 URL에 담아 전송했을 때, 서버가 별도 필터링을 하지 않고 쿼리에 담긴 스크립트를 응답 페이지에 담아 전송하는 것과 같은 경우에 발생한다.
예를 들면…
문자로 pretendor.com 라는 링크를 받았다. 근데 알고보니 이 링크가 진짜 pretendor라는 도메인으로 접속하는 게 아니라, 어떤 공격 당할 사이트 (예: navergoogle.com) 에 쿼리 스트링으로 공격성 스크립트()를 담아 요청하게 하고 있는 것이다.
여기서도 공격 당할 사이트가 Reflected XSS 공격에 취약한… 특정 요청이 별도 필터링 없이 그대로 응답 페이지에 노출되게 설계됐기 때문에 공격성 스크립트가 실행되게 되는 것이다.
(+) 세션 하이재킹(Session Hijacking)이란?
말 그대로 세션을 가로챈다는 뜻이다.
세션 관리를 위해 사용되는 Session ID를 스니핑(엿듣기) 이나 무작위 추측 공격(brute-force guessing)을 통해 도용하는 기법이다.
2. 간단하게 Stored XSS 써보기!
- 준비물:
- 내가 만든 어떤 간단한 서버
- 목록과 상세를 조회할 수 있는 API와 페이지가 구현된
- 개체 생성 API가 구현된
- 내가 만든 어떤 간단한 서버
(1) script 태그 넣어서 개체 생성
<script>alert('document.cookie')</script>
이 스크립트를 넣고 개체를 생성해보자.
그리고 조회해보자.
그럼 XSS 공격에 실패할 것이다.
→ 왜냐하면, HTML5에서 이미 script 태그를 innerHTML에 삽입했을 때 실행되지 않도록 방지해 놓았기 때문.
(2) onerror로 우회하기
<img src=x onerror="alert(document.cookie)">
이 스크립트는 정말로 XSS 공격이 가능하다.
script 태그는 실행되지 않도록 방지해둔 상태이지만, onerror로 우회했기 때문이다.
놀랍지요?!
위 예시는 간단한 alert 창을 띄우기만 했지만,
fetch를 통해 몰래 또 다른 요청을 보낸다는 등의 공격도 가능하다.
3. XSS 공격 방지하기
(1) innerHTML 사용을 자제한다.
위에도 언급됐지만, innerHTML이 이런 문제를 발생시킬 수 있기 때문에… 애초에 사용을 자제하자!
textContent, innerText는 스크립트가 주입되지 않는다.
실제로는 어떻게 처리하고 있을까?
네이버 블로그 에디터에 HTML을 작성할 수가 있는데, img onerror로 alert를 띄우게 하는 스크립트를 작성해보면,
입력한 스크립트가 아래와 같이 치환된다. (서버단에서 치환 처리)
<!-- Not Allowed Attribute Filtered ( src="x" ) -->
<img class="__se_object" s_type="attachment" s_subtype="image" jsonvalue="%7B%7D">
Vue.js와 같은 프레임워크를 사용하면 이런 문제가 없을까?
v-html 디렉티브 사용 시, 사용자 입력을 내부적으로 innerHTML과 동일한 방식으로 처리하기 때문에 보안적으로 취약하다.
때문에 공식 문서에도, v-html에는 절대 사용자 입력을 넣게 하지 말라고 당부하고 있다.
(2) 쿠키에 HttpOnly 옵션을 활성화한다.
활성화 시, 스크립트를 통해 document의 쿠키에 접근할 수 없게 된다.
하지만 localStorage 접근은 막을 방법이 없다. → 때문에 localStorage에는 세션ID와 같은 민감한 정보를 저장하지 않는 것이 원래 좋다.
(3) XSS 특수문자를 치환한다.
FROM | & | < | > |
TO | & | < | > |
입력되는 문자들을 위와 같이 치환해주면, 스크립트로서 실행을 방지하고 문자 그대로 받아올 수 있다.
실제로는 직접 구현하지 않아도 된다. → 오픈소스 라이브러리를 활용하자.
- Lucy XSS Servlet Filter: https://github.com/naver/lucy-xss-servlet-filter
- @RequestBody로 전달되는 JSON 요청에 대해서는 처리해주지 않는다…
- 이에 대한 해결책은 아래 블로그를 참고하자.
'코딩 > WEB 개발' 카테고리의 다른 글
Spring - LLM 연동으로 Gemini를 선택한 이유와 연동 방법! (vs. chatGPT) (0) | 2025.02.25 |
---|---|
Spring - RestTemplate deprecate 예정? (feat. WebClient, RestClient) (1) | 2024.02.04 |
Spring - WebClient 사용법, 주의사항 (0) | 2023.07.25 |
Spring - RestTemplate vs. WebClient (0) | 2023.07.25 |
Spring - @PathVariable 에서 마지막 "~.com"이 짤리는 현상에 대해 (0) | 2023.07.19 |