“반사형 XSS” 쉽게 이해하기, innerHTML 한 줄이 위험한 이유
들어가며
웹 사이트를 개발하다 보면 URL의 파라미터를 화면에 보여주는 경우가 있습니다.
검색 결과를 보여줄 때 검색어를 다시 출력하거나, 로그인 실패 메시지에 사용자가 입력한 값을 표시하는 경우가 대표적입니다.
예를 들어 이런 URL이 있다고 해보겠습니다.
https://example.com/search?keyword=축구
서버에서는 keyword 값을 받아서 검색 결과 화면에 다시 보여줄 수 있습니다.
정상적인 상황에서는 아무 문제가 없어 보입니다.
그런데 여기서 사용자가 입력한 값을 아무런 처리 없이 HTML 화면에 그대로 출력한다면 이야기가 달라집니다.
이때 발생할 수 있는 대표적인 웹 보안 취약점이 바로 반사형 XSS(Reflected XSS)입니다.
반사형 XSS란?
XSS는 Cross-Site Scripting의 약자로, 공격자가 웹 페이지에 악성 스크립트가 실행될 수 있는 입력값을 전달하는 공격 기법입니다.
그중 반사형 XSS는 이름 그대로 사용자의 입력값이 서버에서 처리된 뒤 즉시 웹 페이지에 다시 반사되어 출력되는 형태를 말합니다.
쉽게 생각하면 다음과 같은 흐름입니다.
사용자 입력
↓
URL 파라미터 전달
↓
웹 서버가 입력값 처리
↓
HTML 응답에 입력값 그대로 포함
↓
브라우저가 HTML/스크립트로 해석
핵심은 서버가 받은 데이터를 단순한 문자열로 취급하지 않고, 브라우저가 실행 가능한 HTML 문맥에 그대로 넣어버리는 데 있습니다.
검색 기능에서도 발생할 수 있습니다
검색 페이지를 예로 들어보겠습니다.
사용자가 검색창에 축구를 입력하면 서버가 다음과 같은 HTML을 만들어 줄 수 있습니다.
<h2>검색 결과</h2>
<p>'축구' 검색 결과입니다.</p>
여기까지는 정상입니다.
그런데 검색어를 HTML에 직접 삽입하는 방식으로 구현되어 있다면 공격자가 일반적인 검색어가 아닌 HTML 또는 스크립트 문법이 포함된 값을 전달할 수 있습니다.
예를 들어 테스트 환경에서는 다음과 같이 아주 단순한 문자열을 이용해 XSS 취약점 여부를 확인할 수 있습니다.
<script>alert('XSS')</script>
취약한 페이지에서 이 값이 HTML 문맥에 그대로 삽입되고 브라우저가 이를 코드로 해석한다면 스크립트가 실행될 수 있습니다.
실제 서비스에서는 이런 단순한 테스트보다 훨씬 다양한 형태의 공격이 사용될 수 있기 때문에 중요한 것은 특정 문자열 하나를 차단하는 것이 아닙니다.
사용자 입력을 브라우저에 어떤 방식으로 출력하고 있는지를 확인하는 것이 핵심입니다.
반사형 XSS와 저장형 XSS의 차이
XSS를 공부하다 보면 반사형 XSS와 함께 저장형 XSS라는 용어가 자주 등장합니다.
둘의 가장 큰 차이는 사용자 입력값이 어디에 저장되는가입니다.
반사형 XSS는 일반적으로 사용자의 요청에 포함된 값이 서버의 응답에 바로 포함됩니다.
사용자
↓
악성 입력이 포함된 요청
↓
서버
↓
HTML 응답
↓
브라우저
반면 저장형 XSS는 공격자가 입력한 값이 게시판이나 댓글, 프로필 등의 데이터베이스에 저장될 수 있습니다.
사용자
↓
악성 입력
↓
서버
↓
DB 저장
↓
다른 사용자가 페이지 방문
↓
브라우저에서 출력
따라서 반사형 XSS는 URL의 검색어나 파라미터, 오류 메시지 등 요청값을 다시 화면에 보여주는 기능을 확인하는 것이 중요합니다.
왜 반사형 XSS가 발생할까?
가장 흔한 원인은 사용자 입력값을 HTML에 그대로 출력하는 것입니다.
예를 들어 서버 코드에서 다음과 같은 구조를 가지고 있다고 생각해 보겠습니다.
String keyword = request.getParameter("keyword");
String html =
"<h2>검색 결과</h2>" +
"<p>" + keyword + "</p>";
개발자 입장에서는 단순히 검색어를 화면에 표시하는 코드입니다.
하지만 keyword는 사용자가 직접 입력할 수 있는 값입니다.
따라서 이 값을 HTML 문맥에 그대로 넣으면 브라우저가 해당 문자열을 단순한 데이터가 아니라 HTML 요소로 해석할 가능성이 생깁니다.
결국 문제의 핵심은 이것입니다.
사용자 입력 = 신뢰할 수 없는 데이터
그런데 개발자가 이것을
HTML 코드 = 신뢰할 수 있는 코드
처럼 취급해버리는 순간 문제가 발생할 수 있습니다.
HTML Escape가 중요한 이유
반사형 XSS를 방어할 때 중요한 방법 중 하나가 출력 시점의 HTML Escape입니다.
예를 들어 사용자가 입력한 <와 > 같은 문자를 브라우저가 HTML 태그로 해석하지 못하도록 적절하게 변환하는 방식입니다.
개념적으로 보면 다음과 같습니다.
입력값
<script>
↓
HTML Escape
<script>
↓
브라우저 출력
문자열로 표시
이렇게 하면 브라우저는 <script>를 실제 HTML 태그로 처리하는 것이 아니라 화면에 표시해야 할 문자열로 인식하게 됩니다.
중요한 점은 단순히 입력 단계에서 특정 문자를 제거하는 것과는 다릅니다.
데이터가 어떤 문맥에서 출력되는지를 기준으로 적절한 인코딩을 적용해야 합니다.
프론트엔드에서도 조심해야 합니다
요즘은 서버에서 HTML을 직접 만드는 경우뿐만 아니라 JavaScript로 화면을 구성하는 경우도 많습니다.
예를 들어 다음과 같은 코드는 주의가 필요합니다.
const keyword = location.search;
document.querySelector("#result").innerHTML = keyword;
innerHTML은 문자열을 HTML로 해석하기 때문에 외부에서 들어오는 값을 그대로 넣는 방식은 위험할 수 있습니다.
단순히 텍스트를 표시하려는 목적이라면 상황에 따라 textContent와 같은 텍스트 전용 API를 사용하는 것이 더 적절합니다.
document.querySelector("#result").textContent = keyword;
이런 차이는 코드 한 줄 정도로 보이지만 보안 측면에서는 상당히 중요합니다.
입력값 필터링만으로 해결하면 안 되는 이유
XSS를 막기 위해 특정 문자열을 찾아서 삭제하는 방식만 사용하는 경우가 있습니다.
예를 들어 script라는 단어가 있으면 제거하는 식입니다.
하지만 이런 방식은 근본적인 해결책이 되기 어렵습니다.
HTML에는 다양한 문맥이 존재하고, 브라우저가 입력값을 해석하는 방식도 상황에 따라 달라질 수 있기 때문입니다.
그래서 보안 코드를 작성할 때는
“공격 문자열을 어떻게 차단할까?”
보다
“사용자 입력을 안전한 데이터로 어떻게 출력할까?”
라는 관점으로 접근하는 것이 좋습니다.
반사형 XSS를 점검할 때 확인할 부분
기존 웹 사이트의 보안 취약점을 점검한다면 먼저 사용자 입력이 화면에 다시 출력되는 기능부터 찾아보는 것이 좋습니다.
예를 들면 다음과 같습니다.
검색어
URL 파라미터
오류 메시지
게시판 검색
로그인 실패 메시지
페이지 이동 파라미터
그리고 해당 값이 서버 응답의 HTML에 그대로 들어가는지 확인합니다.
프론트엔드 코드에서는 특히 다음과 같은 API 사용 여부도 살펴볼 필요가 있습니다.
element.innerHTML = userInput;
또는 HTML을 문자열로 만들어 DOM에 삽입하는 코드가 있는지 확인합니다.
반대로 단순 텍스트 출력이라면 텍스트 전용 API나 프레임워크에서 제공하는 안전한 데이터 바인딩 방식을 사용하는 것이 좋습니다.
CSP도 함께 고려할 수 있습니다
웹 애플리케이션에서는 Content Security Policy(CSP) 같은 추가적인 보안 정책도 활용할 수 있습니다.
CSP는 브라우저가 어떤 종류의 리소스나 스크립트를 허용할지 정책으로 제한하는 방식입니다.
다만 CSP를 적용했다고 해서 애플리케이션 코드의 XSS 취약점이 사라지는 것은 아닙니다.
기본적으로는 사용자 입력과 HTML을 안전하게 분리하고, 적절한 출력 인코딩을 적용하는 것이 우선입니다.
CSP는 여기에 추가적인 방어 계층을 구성하는 방식으로 생각하는 것이 좋습니다.
마무리
반사형 XSS는 처음 보면 복잡한 해킹 기법처럼 느껴질 수 있습니다.
하지만 원리를 단순하게 보면 그렇게 어렵지 않습니다.
사용자가 입력한 값이 서버로 전달되고, 그 값이 다시 HTML 응답에 포함되면서 브라우저에서 코드로 해석되는 것.
이것이 반사형 XSS를 이해하는 핵심입니다.
특히 검색 페이지나 오류 메시지처럼 사용자가 입력한 값을 다시 화면에 보여주는 기능을 개발할 때 주의해야 합니다.
그리고 XSS를 방어할 때 특정 공격 문자열 몇 개를 막는 것에만 집중하기보다는 사용자 입력은 기본적으로 신뢰하지 않고, 출력되는 HTML 문맥에 맞게 안전하게 처리한다는 원칙을 가지고 접근하는 것이 중요합니다.
결국 개발자가 기억해야 할 것은 간단합니다.
사용자가 입력한 문자열을 코드처럼 다루지 말 것.
이 원칙 하나를 제대로 이해하고 있어도 XSS를 예방하는 데 상당히 큰 도움이 됩니다.
홍TV



![Eclipse에서 갑자기 "[m2e] Lifecycle Mapping" 오류? Maven 프로젝트가 빨간 줄 뜨는 이유 4 Eclipse에서 갑자기 [m2e] Lifecycle Mapping 오류? Maven 프로젝트가 빨간 줄 뜨는 진짜 이유](https://hongtv.co.kr/wp-content/uploads/2026/09/62edeb0f-aaf8-42cc-af31-1a91e54bc187-300x200.png)
댓글 0
첫 댓글을 남겨보세요.