안녕하세요! 👋 웹 보안의 세계를 탐험하는 여러분, 오늘은 2017년 OWASP Top 10에서 7위를 차지했으며, 웹 취약점의 대명사처럼 불리는 A7: 크로스 사이트 스크립팅(Cross-Site Scripting, XSS)에 대해 알아보겠습니다.

"게시판에 글만 썼을 뿐인데 내 계정이 해킹당했다고?" 이게 어떻게 가능한 일인지, XSS의 교묘한 함정과 그것을 막는 방법을 지금부터 알기 쉽게 설명해 드릴게요! 🕵️♂️
🤔 크로스 사이트 스크립팅(XSS)이란?
XSS는 공격자가 악의적인 스크립트(주로 JavaScript)를 웹 애플리케이션에 삽입하여, 다른 사용자의 브라우저에서 해당 스크립트가 실행되게 만드는 공격입니다.
이름에 '크로스 사이트(Cross-Site)'가 붙어있지만, 공격은 사실 사용자의 브라우저라는 신뢰된 공간 안에서 이루어집니다. 웹사이트는 원래 사용자의 입력을 '데이터'로 취급해야 하는데, 이 데이터를 '코드'로 잘못 해석하고 실행하면서 문제가 발생합니다.
마치 누군가 "이건 그냥 설탕이야"라고 속여서 준 독약을, 웹사이트가 "몸에 좋은 설탕이네!"라며 다른 사용자에게 먹이는 것과 같습니다. 사용자의 브라우저는 웹사이트를 신뢰하기 때문에, 그 웹사이트가 전달해 준 악성 스크립트를 아무런 의심 없이 실행해 버리는 거죠. 💉
XSS의 세 가지 얼굴: 공격 유형 파헤치기
XSS는 악성 스크립트가 어디에 저장되고 어떻게 실행되느냐에 따라 크게 세 가지 유형으로 나뉩니다.
1. 저장 XSS (Stored XSS)
- 위험도: 🥇 (가장 높음)
저장 XSS는 공격자가 삽입한 악성 스크립트가 웹사이트의 데이터베이스나 파일 시스템에 영구적으로 저장될 때 발생합니다. 공격 스크립트가 서버에 저장되어 있기 때문에, 해당 페이지를 방문하는 모든 사용자는 지속적으로 공격에 노출됩니다.
예시: 허술한 게시판 📝
- 공격자가 게시판에 <script>alert('해킹 성공!');</script> 라는 내용을 포함한 글을 작성합니다.
- 웹사이트는 이 내용을 아무런 필터링 없이 데이터베이스에 저장합니다.
- 다른 사용자가 이 게시글을 클릭하여 읽습니다.
- 사용자의 브라우저는 데이터베이스에서 글 내용을 가져와 화면에 표시하는 과정에서 <script> 태그를 코드로 인식하고 실행합니다.
- 사용자의 화면에 '해킹 성공!'이라는 경고창이 뜹니다. (실제 공격에서는 쿠키 탈취, 다른 사이트로 리디렉션 등의 악성 코드가 실행됩니다.)
2. 반사 XSS (Reflected XSS)
- 위험도: 🥈 (중간)
반사 XSS는 악성 스크립트가 서버에 저장되지 않고, 사용자의 요청(Request)에 포함되어 서버로 전송되었다가, 서버의 응답(Response)에 그대로 포함되어 사용자에게 되돌아와(반사되어) 실행되는 방식입니다.
공격자는 주로 이메일 피싱이나 메신저를 통해 악성 스크립트가 포함된 URL을 사용자가 직접 클릭하도록 유도해야 합니다.
예시: 검색 결과 페이지의 허점 🔍
- 공격자가 악성 스크립트가 담긴 URL을 만듭니다: https://example.com/search?q=<script>alert('악성코드 실행!');</script>
- 공격자는 이 URL을 "이벤트 당첨! 확인하세요!"와 같은 미끼 메시지와 함께 피해자에게 보냅니다.
- 피해자가 URL을 클릭하면, <script>...</script> 부분이 검색어(q)로 서버에 전달됩니다.
- 서버는 검색 결과 페이지에 " <script>...</script>에 대한 검색 결과가 없습니다." 와 같이 사용자가 입력한 값을 그대로 포함하여 응답을 보냅니다.
- 피해자의 브라우저는 이 응답을 받아 <script> 태그를 실행하게 됩니다.
3. DOM 기반 XSS (DOM-based XSS)
- 위험도: 🥉 (상대적으로 낮지만 교묘함)
DOM 기반 XSS는 서버와는 전혀 통신하지 않고, 오직 사용자의 브라우저 내에서 발생하는 공격입니다. URL의 프래그먼트(#) 부분과 같이 서버에 전달되지 않는 값을 JavaScript가 동적으로 처리하여 DOM(문서 객체 모델)을 변경할 때 발생합니다.
예시: 동적인 환영 메시지 👋
- 웹사이트에 https://example.com/#username=Paul 처럼 URL의 # 뒤에 오는 이름을 가져와 "Welcome, Paul!" 이라고 표시해주는 JavaScript 코드가 있다고 가정합시다.
- 공격자가 악성 스크립트가 담긴 URL을 만듭니다: https://example.com/#username=<img src=x onerror=alert('DOM XSS!')
- 피해자가 이 URL을 클릭하면, 브라우저의 JavaScript는 URL에서 사용자 이름 부분을 가져옵니다.
- JavaScript는 "Welcome, <img src=x onerror=alert('DOM XSS!')>" 라는 HTML을 동적으로 생성하여 페이지에 삽입합니다.
- 브라우저는 이 HTML을 렌더링하다가 존재하지 않는 이미지(src=x) 로딩에 실패하고, onerror 이벤트에 담긴 악성 스크립트를 실행합니다.
🛡️ XSS 공격, 이렇게 막을 수 있습니다!
XSS를 막는 핵심 원칙은 "사용자의 모든 입력을 의심하고, 출력하기 전에 반드시 소독(Sanitize)하라"는 것입니다.
1. 출력 인코딩/이스케이핑 (Output Encoding/Escaping)
가장 중요하고 효과적인 방어책입니다. 사용자 입력값을 브라우저에 표시하기 전에, HTML에서 특별한 의미를 갖는 문자들(<, >, ", ', & 등)을 안전한 문자 엔티티(<, >, ", ', &)로 변환하는 것입니다.
이렇게 하면 브라우저는 <script>를 태그로 인식하지 않고, 화면에 그대로 "<script>"라는 문자열로만 표시하게 됩니다.
2. 입력값 검증 (Input Validation)
사용자가 입력한 데이터가 예상한 형식(숫자, 영문, 이메일 주소 등)에 맞는지 서버 측에서 검증합니다. 스크립트 태그와 같이 허용되지 않는 패턴이 포함된 입력은 처음부터 차단하는 것이 좋습니다.
3. 콘텐츠 보안 정책 (CSP, Content Security Policy) 사용
CSP는 브라우저에 신뢰할 수 있는 스크립트 소스를 알려주어, 허용되지 않은 출처의 스크립트가 실행되는 것을 막는 강력한 보안 헤더입니다. 인라인 스크립트 실행을 금지하는 등 XSS 공격을 완화하는 데 큰 도움이 됩니다.
4. 최신 프레임워크 사용 및 보안 기능 활용
React, Angular, Vue.js와 같은 최신 웹 프레임워크들은 대부분 XSS를 방어하기 위한 자동 인코딩 기능을 기본적으로 내장하고 있습니다. 이런 프레임워크의 보안 기능을 적극적으로 활용하는 것이 좋습니다.
2021년 OWASP Top 10에서는 XSS가 A3: 인젝션(Injection) 카테고리의 일부로 통합되었습니다. 이는 XSS가 결국 신뢰할 수 없는 사용자 입력을 애플리케이션이 코드로 해석하여 발생하는 '인젝션'의 한 종류임을 명확히 한 것입니다.
XSS는 아주 오래된 공격 기법이지만, 웹 기술이 발전함에 따라 여전히 새로운 형태로 나타나고 있습니다. 개발자는 항상 출력되는 모든 데이터를 경계하는 습관을 가져야 합니다.
'일반IT > IT보안' 카테고리의 다른 글
| 🧱 튼튼한 집도 주춧돌이 썩었다면? OWASP A9:2017 - 알려진 취약점이 있는 구성요소 사용 (5) | 2025.08.16 |
|---|---|
| 🎁 "선물 상자를 열었더니 폭탄이!" OWASP A8:2017 - 안전하지 않은 역직렬화 (4) | 2025.08.16 |
| 🔩 빠진 나사 하나가 모든 것을 무너뜨린다: OWASP A6:2017 - 잘못된 보안 구성(Security Misconfiguration) (3) | 2025.08.16 |
| 🚪"출입금지" 팻말이 무색할 때: OWASP A5:2017 - 취약한 접근 통제(Broken Access Control) (5) | 2025.08.16 |
| 📜 오래된 문서의 역습! OWASP A4:2017 - XML 외부 개체(XXE) 파헤치기 (3) | 2025.08.16 |