[2025-08-18] DOM Vulnerability
🦥 본문
정의 및 개념
클라이언트 측에서 동작하는 JS 코드가 DOM을 잘못 다루면서 발생하는 보안 취약점
- DOM : Document Object Model. 브라우저가 HTML 문서를 관리하기 위해 사용하는 객체 모델로 웹 페이지에 대한 프로그래밍 인터페이
공격 방법: DOM Clobbering
공격자가 DOM 요소의 식별자 속성(id
, name
)을 이용해 자바스크립트에서 DOM 객체들의 속성을 변조하는 기법.
- 예시
-
EX) 별도 변수 정의 없이 link1에 접근하면 해당 요소를 DOM에서 찾음
<!DOCTYPE html> <html> <body> <a id="link1" href="https://host">host</a> <script> // 브라우저 콘솔에서 확인할 수 있는 상황 재현 console.log(document.body); // link1은 DOM Element로 접근 가능 console.log(link1); // 자바스크립트 변수로 link1 재정의 var link1 = 1; console.log(link1); // 1 출력 </script> </body> </html>
-
EX2) test 변수를 통한 DOM 객체 접근
<!DOCTYPE html> <html> <body> <input id="test"> <script> // 현재 body 출력 console.log(document.body); // 처음 input의 value는 빈 문자열 console.log(document.getElementById("test").value); // '' // id="test" 이므로 전역 객체 test가 자동으로 생성됨 // 따라서 test.value를 직접 할당 가능 test.value = 1234; console.log(test.value); // 1234 // 실제 DOM 요소에도 값이 반영됨 console.log(document.getElementById("test").value); // '1234' </script> </body> </html>
-
EX3) form1 변수를 통한 DOM 객체 접근
<!DOCTYPE html> <html> <body> <form id="form1"> <input name="firstName" value="John"> <input type="submit"> </form> <script> // form1 내부에 있는 input name="firstName" 접근 console.log(document.getElementById("form1").firstName.value); // 'John' // form1 객체는 DOM에서 자동으로 전역 변수화됨 // 또한 form1의 하위 input 요소는 name 속성을 통해 프로퍼티로 접근 가능 form1.firstName.value = 'Alice'; console.log(form1.firstName.value); // 'Alice' // 다시 DOM으로 확인해도 값 변경이 반영됨 console.log(document.getElementById("form1").firstName.value); // 'Alice' </script> </body> </html>
-
글로별 변수 이름공간이나 요소 객체 속성은 미리 정의된 속성 / 함수(element.innerHTML
, window.open
)와 충돌
-
EX)
innerHTML
,removeChild
와의 충돌<!DOCTYPE html> <html> <body> <form> <input id="innerHTML"> <input type="text" name="removeChild"> </form> <script> // form 내부 구조 확인 console.log(document.forms[0].innerHTML); // 출력: // <input id="innerHTML"> // <input type="text" name="removeChild"> // 원래 removeChild는 DOM 메서드인데, // input의 name="removeChild" 때문에 동일한 프로퍼티가 생성됨 // 따라서 메서드 대신 input 요소가 반환됨 console.log(document.forms[0].removeChild); // 출력: <input type="text" name="removeChild"> </script> </body> </html>
- 기존에는 HTML 내의 콘텐츠를 String으로 반환하지만 객체 요소 자체를 반환
웹 애플리케이션이 미리 정의되지 않은 전역 변수에 접근한다면 공격자가 입력한 요소로 대체되어 반환될 수 있음
- 브라우저는 HTML 요소의
id
나name
을 전역 변수(window.속성
)로 자동 등록- EX)
<input id=”test”>
가 있으면window.test
가 자동 생성. 하지만 개발자가 test라는 변수를 쓰려고 하는 데 정의하지 않은 경우 공격자가 HTML에서id=”test”
를 넣어버려서 DOM 요소 반환
- EX)
form 등 요소에서 속성을 접근할 때 본래 속성 값이 아닌 삽입된 요소가 반환될 수 있음
<form>
객체에는removeChild
,submit
,action
같은 속성이 있음- EX) 공격자가
<input name=”removeChild”>
를 넣으면 객체가 반환
- EX) 공격자가
실습: DOM Clobbering
<!-- HTML CODE IS INJECTED HERE -->
<!-- Your HTML here -->
<div id="config_status" style="white-space: pre;"></div>
<script>
if (window.CONFIG) {
if (CONFIG.redirectUrl) {
location.href = CONFIG.redirectUrl
} else {
document.write("<h1>redirectUrl is empty</h1>")
}
} else {
document.write("<h1>CONFIG is not defined.</h1>")
}
status = "CONFIG: " + (window.CONFIG||"NOT DEFINED")
status += "\r\n"
status += "CONFIG.redirectUrl: "
status += (window.CONFIG?window.CONFIG.redirectUrl||"NOT DEFINED":"NOT DEFINED")
config_status.textContent = status
</script>
-
답)
<a id="CONFIG" name="redirectUrl" href="javascript:alert(1);"></a> <a id="CONFIG"></a>
- window.CONFIG 까지는 첫 번째 줄로도 괜찮음
- CONFIG.reidectURL인 경우는 첫 번째 줄로 안됨
→ HTMLCollection을 이용하여 빈 줄을 두면 다음과 같이 HTMLCollection 생성
HTMLCollection(2) [a#CONFIG, a#CONFIG, CONFIG: a#CONFIG, redirectUrl: a#CONFIG]
CONFIG.redirectURL을 통해 해당 객체에 접근 가능
공격 방법: DOM-based XSS
클라이언트 측 자바스크립트 코드가 사용자 입력을 DOM에 삽입하면서 발생하는 XSS 취약점
-
EX)
var name_el = document.getElementById("name"); name_el.innerHTML = `My name is ${decodeURIComponent(location.hash.slice(1))}.`;
- 해시값에
https://host/domxss.html#<img src=@ onerror=alert(1)>
을 삽입하는 경우 XSS 발생 innerHTML
로 삽입하는 경우<script>
태그는 실행 불가하므로 event 핸드러 사용
- 해시값에
대응 방법
- DOM Clobbering
- 간접 메소드 호출 및 접근자 사용(
Function#call
)-
EX)
// form.reset() 대신 HTMLFormElement.prototype.reset.call(formElement); // el.textContent = '' 대신 Object.getOwnPropertyDescriptor(Node.prototype, 'textContent') .set.call(el, '');
- Third-party 라이브러리(jQuery)와 조합하면 취약점 발생 쉬움
-
- DOMPurify 같은 sanitization 라이브러리 사용 :
id
,name
같은 속성 제거
- 간접 메소드 호출 및 접근자 사용(
- DOM-based XSS
- 동적으로 HTML 추가하는 행위 지양,
innerHTML
대신innerText
추가
- 동적으로 HTML 추가하는 행위 지양,
Leave a comment