flawopen.com/XSS in JavaScript/DOMPurify and innerHTML
DOMPurify만으로 innerHTML이 안전해지나요?
DOMPurify는 결과를 나중에 바꾸지 않고 HTML sink에 바로 넣을 때 리치 HTML을 위한 강력한 허용 목록 sanitizer입니다. 입고장에서 검사한 소포와 같습니다. 이후 다른 과정에서 다시 포장하거나 다른 경로로 넣으면 최초 검사가 최종 내용을 보장하지 않습니다. 일반 텍스트는 textContent로 넣어 HTML 파싱을 피하세요.
// 저장 시 오래된 버전/정책으로 정화함. saveToDb(DOMPurify.sanitize(input)); // 나중에 저장 문자열을 재검증 없이 신뢰함. el.innerHTML = fetchedFromDb;
// 리치 HTML이 필요하면 최신 라이브러리로 정화 // HTML 컨텍스트에 넣기 직전에 수행함. el.innerHTML = DOMPurify.sanitize(untrustedHtml, { USE_PROFILES: { html: true }, }); // 일반 텍스트는 HTML로 파싱하지 않음. textNode.textContent = untrustedText;
저장 시 한 번만 정화하는 방식이 약한 이유
저장 시 정화가 자동으로 취약한 것은 아니지만 오래된 데이터 경계를 만듭니다. sanitizer 수정, 정책 변경, 정화를 건너뛴 경로 뒤에도 렌더러가 오래된 결과를 신뢰할 수 있습니다. 렌더링 시 현재 버전과 정확한 sink 정책으로 다시 정화하세요. 삽입 전에 정화된 마크업을 바꾸거나 다른 라이브러리에 넘기지 마세요. 후처리가 정화 효과를 무효화할 수 있습니다.
설정과 출력 컨텍스트가 중요합니다
DOMPurify 기본 설정은 HTML, SVG, MathML을 허용합니다. HTML만 필요하면 USE_PROFILES: { html: true }로 범위를 줄이세요. 검토 없이 태그나 속성을 추가하지 마세요. DOMPurify는 CSS sanitizer가 아니며 모든 외부 리소스 요청을 막지 않습니다. 결과를 HTML 컨텍스트에만 쓰고 JavaScript, 속성, XML, SVG, CSS에는 재사용하지 마세요.
Trusted Types와 서버 렌더링
지원되는 환경에서는 Content Security Policy로 Trusted Types를 적용해 승인된 정책을 거치지 않은 문자열을 HTML sink에서 거부하세요. DOMPurify는 TrustedHTML을 반환할 수 있지만 위험한 설정이나 후처리를 해결하지는 않습니다. 서버에서는 DOM 구현에 의존하므로 jsdom을 최신 상태로 유지하고 보안 민감한 정화에 happy-dom을 함께 쓰지 마세요.
운영 체크리스트
- ✓일반 텍스트에는 textContent를 사용하세요. 제한된 리치 HTML이 필요한 경우에만 DOMPurify를 사용하세요.
- ✓최종 HTML sink에서 최신 DOMPurify와 검토된 최소 허용 목록으로 정화하고, 이후 결과를 변경하지 마세요.
- ✓출력은 HTML 컨텍스트에만 두세요. JavaScript, CSS, 속성, XML, SVG 또는 다른 파서 컨텍스트에 재사용하지 마세요.
- ✓영구 setConfig(), hooks, ADD_TAGS/ADD_ATTR, 서버 DOM 버전, Trusted Types 정책/CSP를 함께 검토하세요.
HTML injection sink 검색
rg -n -g '*.{js,ts,jsx,tsx}' '(innerHTML|outerHTML|insertAdjacentHTML|document\.write|dangerouslySetInnerHTML)' .