1. XSS(Cross-site scripting)에 대해서 알아보기전에..
부끄럽게도 XSS에 대해서 모르고 늘 실무를 진행해왔었습니다. XSS에 대해서 두 세마디로 대충 설명하는 정도밖에 되지 못했습니다.
사람마다 배움의 방식은 각기 다릅니다. 저는 직접 경험을 하고 혼나야 배우는 경향이 있습니다. 이번에 직접 간단한 페이지를 만들고 직접 XSS 공격을 해보면서 알아보았습니다.
혹여 저와 같이 XSS에 대해서 사전적인 의미만 숙지한채 개발을 하고 계셨다면 잠시 시간 내서 직접 만들어보며 알아보는 과정을 함께 해도 좋겠습니다.
2. XSS(Cross-site scripting)이란?
MDN에서 사전적 의미는 아래와 같습니다. 저는 여지껏 정의가 추상적이라고 느껴왔습니다. 이제 직접 공격을 해보겠습니다.
크로스 사이트 스크립팅 (XSS)은 공격자가 웹사이트에 악성 클라이언트 사이드 코드를 삽입할 수 있도록 하는 보안 취약점 공격입니다. 이 악성 코드는 피해자에 의해 실행되며 공격자가 접근 제어를 우회하고 사용자로 위장할 수 있게 만들어 줍니다. 오픈 웹 애플리케이션 보안 프로젝트에 따르면, XSS는 2017년에 7번째로 흔한 웹 앱 취약점이었습니다.
웹 앱이 충분한 유효성 검사나 인코딩을 사용하지 않으면 이러한 공격은 성공하게 됩니다. 사용자의 브라우저는 신뢰할 수 없는 악성 스크립트를 탐지할 수 없고, 쿠키, 세션 토큰 또는 기타 민감한 사이트별 정보에 대한 접근 권한을 부여해버리거나 악성 스크립트가 HTML 콘텐츠를 다시 작성할 수 있도록 합니다.
3. 공격할 대상
공격을 하려면 대상이 필요합니다. 웹서비스에서 흔하게 있는 기능을 하나 만들었습니다. 검색어를 등록하면 검색값을 innerHTML을 통하여 삽입합니다. (직접 검색해볼 수 있도록 codesandbox로 구현했습니다.)
4. img 태그로 공격
만약에 아래 이미지 스크립트를 그대로 검색해보면 어떻게 될까요? 아래 태그를 직접 검색창에 추가하고 확인을 눌러보시면 알 수 있습니다.
# src에 의미없는 값을 넣어서 일부러 에러를 터뜨려 alert를 콜하는 이미지태그입니다.
# 만약에 alert가 아닌 웹사이트의 개인정보들을 탈취할 수 있는 js코드를 넣으면 어떻게 될까요?
<img src='x' onerror='alert("공격")'>
직접 해보셨나요? 아래처럼 검색값이 나오지 않고 alert가 나왔습니다.
아래 html 코드가 어떻게 변경 되었는지 보면 더 명확히 알 수 있습니다. innerHTML을 사용했기 때문에 내부에 img태그가 추가되었습니다.
4. script 태그로 공격
img 태그로 공격을 성공했으니 이번에는 script 태그로 공격 해볼까요?
# 생으로 script에서 alert 공격을 해보시겠습니까?
<script>alert("script 공격");</script>
아무일도 일어나지 않습니다. html을 까보면 script가 잘 삽입되어있습니다. 그런데 왜 아무일도 일어나지 않을까요?
이유는 정책상 innerHTML을 통해 삽입된 script 태그는 실행되지 않습니다. 명확하게 보안상의 이유라고 명시 되어있지 않지만, 분명 보안상의 이유 때문일거라고 생각합니다. 아래 가이드라인 참조 바랍니다.
MDN Element innerHTML property:
W3 DOM Guideline:
5. escape을 통한 방어와 리액트
XSS 방어 방법에 여러가지가 있겠지만 대표적인 방법은 escape를 시키는 방법이 있습니다. 그러므로 태그를 삽입해도 태그처럼 작동하지 않고 일반 스트링으로 보여지게 됩니다.
# escape을 하게 되면 아래처럼 변환됩니다:
< becomes <
> becomes >
# script 태그는 아래처럼 변환됩니다.
<script> -> <script>
리액트에서 jsx를 변환시킬 때 이 방법을 사용하여 XSS 공격을 방어합니다.
5.1 리액트의 dangerouslySetInnerHTML
우리는 요구사항에 따라 innerHTML이 필요한 경우가 종종 있습니다. 리액트에서는 속성값을 dangerouslySetInnerHTML이라고 설정하여 구현할 수 있습니다. XSS 공격을 직접 해보았다면 왜 리액트에서 innerHTML을 경고하는지 알 수 있습니다.
이 취약점 마저도 DOMPurfiy같은 라이브러리를 사용하여 공격을 막을 수 있습니다. 다만 그냥 사용하는 것과 XSS 공격에 대해 이해를 하고 해당 라이브러리를 사용하는 것은 분명히 차이가 있을 것이라고 생각합니다.
6. 결말
다시 올라가 MDN의 XSS 정의를 다시 읽어본다면 이전보다 깊은 이해가 가능할 것이라 믿습니다. 간단하게나마 직접 XSS 공격을 해보면서 알아보다보니 웹의 취약점에 대해서 시야가 넓어진 경험을 했습니다. 특히나 다양한 XSS 공격방식(reflected, store-based, DOM-based)이라던지 더 넓게는 CSP(Content Security Policy), CSRF(Cross-site Request Forgery) 같은 개념의 이해를 하는데 직간접적으로 도움이 되었습니다.
늘 보안에 관련해 직접 해보지 않고 눈으로만 읽고 지나갔었습니다. 모르긴 몰라도 웹 보안 관련 공부를 하면서 저같이 직접 해보지 않고 주입식으로 외우고 지나가시는 분들이 적지 않을 거라고 생각합니다. 특히나 바쁘게 실무를 쳐내거나 면접 준비를 하는 입장에서는 눈에 보이지 않는 내용이기 때문에 해보기 쉽지 않은 것 같습니다.
이런 의미에서 저같은 분들이 있을까봐, 그리고 저에게도 기록으로 남기고자 글을 작성했습니다.
reference:
- https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting
- https://www.w3.org/TR/2008/WD-html5-20080610/dom.html#innerhtml0
- https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML
- https://www.stackhawk.com/blog/react-xss-guide-examples-and-prevention/
- https://legacy.reactjs.org/docs/introducing-jsx.html#jsx-prevents-injection-attacks
- https://react.dev/reference/react-dom/components/common#dangerously-setting-the-inner-html
- https://github.com/cure53/DOMPurify
- https://portswigger.net/web-security/cross-site-scripting#what-is-cross-site-scripting-xss