← 홈페이지 5강 목록으로
🛡️
EPISODE 01
Stored/Reflected/DOM XSS · innerHTML · DOMPurify · CSP

XSS 방어

XSS의 3가지 유형, innerHTML의 위험성과 textContent로의 안전한 대안, 서버측 이스케이프, DOMPurify로 부분 HTML 허용, 그리고 CSP 헤더로 추가 보호층을 익힙니다.

XSS보안CSPDOMPurify
소요 시간
45분
난이도
📊 중급
선수 조건
🎯 a11y-04
결과물
사용자 입력을 안전하게 렌더링하는 폼·댓글 페이지

이 강의에서 배우는 것

  • 1Stored / Reflected / DOM XSS 의 차이를 안다
  • 2innerHTML 대신 textContent를 사용한다
  • 3DOMPurify로 마크다운/리치텍스트를 안전하게 처리한다
  • 4CSP 헤더와 helmet으로 추가 보호층을 만든다

1. XSS란

공격자가 웹 페이지에 악성 스크립트를 삽입해 다른 사용자 브라우저에서 실행시키는 공격. 쿠키/세션 탈취, 키로거, 피싱 리다이렉트 등.

2. XSS 3가지 유형

Stored XSS — DB에 저장

text
[공격자] 댓글에 <script>document.cookie를 전송</script>
[서버] DB에 저장
[피해자] 페이지 방문 시 자동 실행

Reflected XSS — URL에 반사

text
악성 URL: https://bank.com/search?q=<script>...</script>
공격자가 피해자에게 이메일로 전송 → 서버 응답에 그대로 반사 → 실행

DOM XSS — 클라이언트 JS

javascript
// 취약: URL 해시를 그대로 innerHTML
const hash = location.hash.substring(1);
document.getElementById('output').innerHTML = hash;

// 공격: https://site.com/#<img src=x onerror=악성코드>

3. innerHTML의 위험성

javascript
// ✗ 절대 금지
element.innerHTML = userInput;
// 공격자: '<img src=x onerror="document.cookie 전송">' → XSS!

// ✓ textContent (HTML 태그를 텍스트로)
element.textContent = userInput;
// '<script>...' 가 그대로 화면에 텍스트로 표시

// ✓ createElement + textContent
const p = document.createElement('p');
p.textContent = userInput;
container.appendChild(p);

4. 서버측 이스케이프

javascript
function escapeHtml(str) {
  return str
    .replace(/&/g,  '&amp;')
    .replace(/</g,  '&lt;')
    .replace(/>/g,  '&gt;')
    .replace(/"/g,  '&quot;')
    .replace(/'/g,  '&#x27;');
}

// <script>alert(1)</script>
// → &lt;script&gt;alert(1)&lt;/script&gt;
// → 화면에 텍스트로만 표시

5. DOMPurify

마크다운 에디터처럼 일부 HTML 태그는 허용해야 할 때.

html
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.6/purify.min.js"></script>
javascript
const dirty = '<img src=x onerror=alert(1)> <b>안전한 텍스트</b>';
const clean = DOMPurify.sanitize(dirty);
// 결과: " <b>안전한 텍스트</b>"   ← onerror 제거, 안전한 태그 유지

element.innerHTML = DOMPurify.sanitize(userInput);

6. CSP (Content-Security-Policy)

javascript
app.use((req, res, next) => {
  res.setHeader('Content-Security-Policy',
    "default-src 'self'; " +
    "script-src 'self'; " +
    "style-src 'self' 'unsafe-inline';"
  );
  next();
});

// 권장: helmet
const helmet = require('helmet');
app.use(helmet());

CSP가 있으면 <script>alert(1)</script> 같은 인라인 스크립트가 차단됩니다.

예제 코드 / 강의 자료

전체 강의 자료와 예제 코드는 GitHub에서 자유롭게 받아볼 수 있습니다.

GitHub에서 보기 ↗