DOM 기반의 XSS(Cross-Site Scripting)는 서버와의 직접적인 상호작용 없이 브라우저에서 발생하는 취약점으로, 보안 담당자와 개발자 모두에게 상당한 도전 과제가 될 수 있습니다. 특히 SPA(Single Page Application)와 같이 클라이언트에서 동적으로 많은 작업을 처리하는 환경에서는 DOM-based XSS의 가능성이 더욱 높아지며, 그 탐지와 대응도 복잡해집니다.
DOM-based XSS란?
"DOM-based XSS"는 사용자로부터 입력받은 값이 서버를 거치지 않고 브라우저의 JavaScript 코드에서 DOM 요소에 직접 삽입될 때 발생하는 보안 취약점입니다. 이 과정에서 적절한 필터링이나 이스케이프 처리가 생략되면 악성 스크립트가 실행될 수 있습니다.
주로 다음과 같은 JavaScript API를 통해 공격이 유입됩니다:
location.hash
,location.search
,document.URL
,document.referrer
element.innerHTML
,element.outerHTML
,document.write
,eval()
등
DOM-based XSS는 서버의 응답 자체에 문제가 없더라도, 클라이언트 코드의 설계나 구현 미비로 인해 사용자에게 악영향을 줄 수 있다는 점에서 더욱 주의가 필요합니다.
발생 예시: URL 조작을 통한 DOM 조작
<!-- index.html -->
<div id="output"></div>
<script>
const hash = location.hash.substring(1); // URL의 해시 부분 가져오기
document.getElementById("output").innerHTML = hash;
</script>
위 코드에서 URL이 다음과 같이 조작될 수 있습니다:
https://example.com/index.html#<script>alert("XSS")</script>
이 URL을 통해 사용자가 페이지에 접근하면, 브라우저는 해시 값을 DOM에 삽입하면서 <script>
태그도 함께 실행하게 됩니다. 이처럼 DOM을 다루는 코드가 입력값에 대한 검증 없이 동작할 경우, 클라이언트에서 직접 공격이 발생하게 됩니다.
취약한 코드 vs 안전한 코드 비교
취약한 방식 (innerHTML 사용)
const userInput = location.hash.substring(1);
document.getElementById("msg").innerHTML = userInput;
위 코드에서는 innerHTML
을 통해 사용자 입력을 DOM에 직접 삽입하고 있어 <script>
태그가 포함될 경우 실행됩니다.
안전한 방식 (textContent 사용)
const userInput = location.hash.substring(1);
document.getElementById("msg").textContent = userInput;
textContent
는 HTML 요소로 인식하지 않고 텍스트로 처리하기 때문에, 악성 스크립트가 실행되지 않습니다.
대응 전략
입력값을 직접 DOM에 삽입하지 않기
- 가능하다면
innerHTML
,document.write
,eval()
은 사용하지 않음 textContent
,setAttribute
,createTextNode
등의 안전한 API를 활용
클라이언트 필터링 라이브러리 활용
- DOMPurify와 같은 라이브러리를 통해 DOM 삽입 전 입력값 정제 가능
- 특히 사용자 콘텐츠가 HTML을 포함할 수 있는 상황에서는 필수
const cleanInput = DOMPurify.sanitize(userInput);
document.getElementById("msg").innerHTML = cleanInput;
보안 정책 강화
- CSP(Content Security Policy)를 통해 인라인 스크립트와 외부 리소스를 제한
- 예:
Content-Security-Policy: default-src 'self'; script-src 'self'
는 외부 스크립트 로드를 방지
이러한 정책은 XSS의 영향을 줄이고, 의도치 않은 스크립트 실행을 방지하는 데 큰 도움이 됩니다.
참고 자료
DOM-based XSS는 눈에 띄지 않게 사용자에게 피해를 입힐 수 있는 은밀한 공격입니다. 서버 보안만으로는 완벽한 방어가 불가능하므로, 프론트엔드 개발자는 클라이언트 코드 내 입력값 처리 로직에 각별한 주의를 기울여야 합니다. 특히 사용자의 입력이 화면에 직접 표시되는 경우, 항상 “어떻게든 실행될 수 있다”는 전제를 두고 설계하는 것이 안전한 웹을 만드는 기본입니다.