한양과학기술고등학교

에듀테크 프로젝트 대시보드

XSS 공격 (크로스 사이트 스크립팅)

XSS 공격 — 크로스 사이트 스크립팅

XSS(Cross-Site Scripting)는 공격자가 웹 페이지에 악성 자바스크립트를 끼워 넣어, 그 페이지를 여는 다른 사용자의 브라우저에서 실행시키는 공격입니다. 원인은 대부분 하나 — 사용자가 입력한 값을 이스케이프 없이 HTML에 그대로 출력하는 것. 훔칠 수 있는 건 세션 쿠키, 입력 중인 비밀번호, 페이지 조작(피싱)까지 다양합니다. 아래 가짜 커뮤니티에 댓글을 남겨 보세요. 취약 모드에서는 실제로 스크립트가 돕니다(격리된 iframe).

댓글 게시판 공격 시뮬레이터

alert 팝업 쿠키/세션 탈취 페이지 변조(피싱) <script> (안 먹힘) 평범한 댓글

브라우저에 렌더링된 게시판

서버가 내려보낸 HTML

아직 아무 일도 일어나지 않았습니다. 💬 댓글 남기기 를 눌러 보세요.

💡 왜 <script> 는 안 먹히나요? 브라우저는 innerHTML 로 나중에 삽입된 <script> 는 실행하지 않습니다. 그래서 공격자는 onerror, onload 같은 이벤트 핸들러 속성을 씁니다.

XSS 세 가지 유형

Stored (저장형)

악성 스크립트가 DB에 저장되어, 그 글/프로필을 보는 모든 방문자에게 전달됩니다. 게시판·댓글·닉네임이 표적. 위 데모가 이 유형입니다. 가장 피해가 큽니다.

Reflected (반사형)

검색어·URL 파라미터가 응답에 그대로 되돌아올 때 발생. ?q=<script>… 가 담긴 링크를 피해자가 클릭하게 유도(메일·메신저)합니다.

DOM 기반

서버는 멀쩡한데 클라이언트 JS가 location.hash 등을 받아 innerHTML 에 꽂으면서 터집니다. 서버 로그엔 안 남습니다.

핵심 방어 = HTML 이스케이프(출력 인코딩)

원래 문자HTML 엔티티막는 것
<&lt;태그 시작
>&gt;태그 끝
&&amp;엔티티 시작
"&quot;속성값 탈출
'&#x27;속성값 탈출

이스케이프하면 <img …> 이 태그가 아니라 눈에 보이는 글자 &lt;img …&gt; 로 출력됩니다. 브라우저는 이걸 코드로 해석하지 않습니다. 단, 넣는 위치(context)마다 규칙이 다릅니다 — HTML 본문, 속성값, <script> 안, URL, CSS 는 각각 다르게 인코딩해야 합니다.

실무 방어 체크리스트

  • 출력 시 자동 이스케이프 — Jinja2·React·Vue 는 기본으로 이스케이프한다. | safe, dangerouslySetInnerHTML, v-html 을 함부로 쓰지 않는다.
  • CSP(Content-Security-Policy) 헤더 — script-src 'self' 로 인라인 스크립트·외부 도메인 차단. 한 줄이 뚫려도 실행을 막는 2차 방어선.
  • 쿠키에 HttpOnly — JS 의 document.cookie 로 세션 쿠키를 못 읽게 한다. Secure, SameSite 도 함께.
  • 리치 텍스트는 허용목록 방식 새니타이저(DOMPurify 등)로 태그·속성을 걸러낸다. 블랙리스트 필터는 우회당한다.
  • 입력 검증은 보조 수단 — 근본 해결은 "저장"이 아니라 "출력" 시점의 인코딩이다.

코드로 보는 취약 vs 안전

# ── 취약: 사용자 입력을 문자열로 이어붙여 HTML 생성 ── html = f'<div class="cmt">{comment}</div>' # comment = "<img src=x onerror=alert(1)>" # ── 안전 1: Jinja2 템플릿 (autoescape 기본 ON) ── # templates/board.html <div class="cmt"></div> # < > " ' & 자동 변환 <div class="cmt"></div> # ← 이렇게 끄면 다시 취약 # ── 안전 2: 파이썬에서 직접 이스케이프 ── import html safe = html.escape(comment) # &lt;img src=x onerror=alert(1)&gt; # ── 안전 3: 응답 헤더로 2차 방어 (FastAPI 미들웨어) ── response.headers["Content-Security-Policy"] = "default-src 'self'" response.set_cookie("session", tok, httponly=True, secure=True, samesite="lax")

학습용 인터랙티브 페이지 · 스크립트는 격리된 sandbox iframe 안에서만 실행됩니다 · 한양과학기술고등학교 프로젝트 허브