DOM 기반 교차 사이트 스크립팅 (DOM XSS)은
사용자 제어 소스 (예: 사용자 이름 또는 URL
프래그먼트에서 가져온 리디렉션 URL)의 데이터가 싱크에 도달할 때 발생합니다. 싱크는 eval()과 같은 함수 또는 임의의 JavaScript 코드를 실행할 수 있는 .innerHTML과 같은 속성
설정자입니다.
DOM XSS는 가장 일반적인 웹 보안 취약점 중 하나이며 개발팀이 실수로 앱에 도입하는 경우가 많습니다. 신뢰할 수 있는 유형은 위험한 웹 API 함수를 기본적으로 안전하게 만들어 DOM XSS 취약점이 없는 애플리케이션을 작성하고 보안 검토를 수행하며 유지할 수 있는 도구를 제공합니다. 신뢰할 수 있는 유형은 아직 지원하지 않는 브라우저의 폴리필 로 사용할 수 있습니다.
배경
DOM XSS 는 오랫동안 가장 널리 퍼져 있고 위험한 웹 보안 취약점 중 하나였습니다.
교차 사이트 스크립팅에는 두 가지 종류가 있습니다. 일부 XSS 취약점은 웹사이트를 구성하는 HTML 코드를 안전하지 않게 만드는 서버 측 코드로 인해 발생합니다. 다른 취약점은 클라이언트의 근본 원인이 있으며 JavaScript 코드가 사용자 제어 콘텐츠로 위험한 함수를 호출합니다.
서버 측 XSS를 방지하려면, 문자열을 연결하여 HTML을 생성하지 마세요. 대신 추가 버그 완화를 위해 nonce 기반 콘텐츠 보안 정책 과 함께 안전한 컨텍스트 자동 이스케이프 템플릿 라이브러리를 사용하세요.
이제 브라우저는 신뢰할 수 있는 유형을 사용하여 클라이언트 측 DOM 기반 XSS를 방지하는 데도 도움이 될 수 있습니다.
API 소개
신뢰할 수 있는 유형은 다음과 같은 위험한 싱크 함수를 잠가 작동합니다. 브라우저 공급업체와 웹 프레임워크 는 보안상의 이유로 이러한 기능을 사용하지 못하도록 이미 안내하고 있으므로 일부는 이미 알고 있을 수 있습니다.
- 스크립트 조작:
<script src>및<script>요소의 텍스트 콘텐츠 설정 - 문자열에서 HTML 생성:
- 플러그인 콘텐츠 실행:
- 런타임 JavaScript 코드 컴파일:
evalsetTimeoutsetIntervalnew Function()
신뢰할 수 있는 유형을 사용하려면 이러한 싱크 함수에 전달하기 전에 데이터를 처리해야 합니다. 브라우저에서 데이터가 신뢰할 수 있는지 알 수 없으므로 문자열만 사용하면 실패합니다.
anElement.innerHTML = location.href;
데이터가 안전하게 처리되었음을 나타내려면 특수 객체인 신뢰할 수 있는 유형을 만듭니다.
anElement.innerHTML = aTrustedHTML;
TrustedHTML
객체를 허용합니다. 다른
민감한 싱크를 위한
TrustedScript 및 TrustedScriptURL 객체도 있습니다.
신뢰할 수 있는 유형은 애플리케이션의 DOM XSS 공격 노출 영역 을 크게 줄입니다. 보안 검토를 간소화하고 브라우저에서 런타임에 코드를 컴파일, 린팅 또는 번들링할 때 실행되는 유형 기반 보안 검사를 적용할 수 있습니다.
신뢰할 수 있는 유형을 사용하는 방법
콘텐츠 보안 정책 위반 보고서 준비
오픈소스 reporting-api-processor 또는 go-csp-collector와 같은 보고서 수집기를 배포하거나 상용 제품 중 하나 를 사용할 수 있습니다. ReportingObserver를 사용하여 브라우저에서 맞춤 로깅 및 디버그 위반을 추가할 수도 있습니다.
const observer = new ReportingObserver((reports, observer) => {
for (const report of reports) {
if (report.type !== 'csp-violation' ||
report.body.effectiveDirective !== 'require-trusted-types-for') {
continue;
}
const violation = report.body;
console.log('Trusted Types Violation:', violation);
// ... (rest of your logging and reporting logic)
}
}, { buffered: true });
observer.observe();
또는 이벤트 리스너를 추가하여
document.addEventListener('securitypolicyviolation',
console.error.bind(console));
보고 전용 CSP 헤더 추가
신뢰할 수 있는 유형으로 이전하려는 문서에 다음 HTTP 응답 헤더를 추가합니다.
Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
이제 모든 위반이 //my-csp-endpoint.example에 보고되지만 웹사이트는 계속 작동합니다. 다음 섹션에서는 //my-csp-endpoint.example이 작동하는 방식을 설명합니다.
신뢰할 수 있는 유형 위반 식별
이제부터 신뢰할 수 있는 유형이 위반을 감지할 때마다 브라우저는 구성된 report-uri로 보고서를 전송합니다. 예를 들어 애플리케이션이 문자열을 innerHTML에 전달하면 브라우저는 다음 보고서를 전송합니다.
{
"csp-report": {
"document-uri": "https://my.url.example",
"violated-directive": "require-trusted-types-for",
"disposition": "report",
"blocked-uri": "trusted-types-sink",
"line-number": 39,
"column-number": 12,
"source-file": "https://my.url.example/script.js",
"status-code": 0,
"script-sample": "Element innerHTML <img src=x"
}
}
이는 39번째 줄의 https://my.url.example/script.js에서 <img src=x로 시작하는 문자열로 innerHTML이
호출되었다고 말합니다. 이 정보를 사용하면 DOM XSS를 도입하고 변경해야 할 수 있는 코드 부분을 좁힐 수 있습니다.
위반 수정
신뢰할 수 있는 유형 위반을 수정하는 방법에는 몇 가지가 있습니다. 위반 코드를 삭제하거나 , 라이브러리를 사용하거나, 신뢰할 수 있는 유형 정책을 만들거나 최후의 수단으로 기본 정책을 만들 수 있습니다.
위반 코드 재작성
비준수 코드가 더 이상 필요하지 않거나 위반을 일으키는 함수 없이 다시 작성할 수 있습니다.
el.textContent = ''; const img = document.createElement('img'); img.src = 'xyz.jpg'; el.appendChild(img);
el.innerHTML = '<img src=xyz.jpg>';
라이브러리 사용
일부 라이브러리는 이미 싱크 함수에 전달할 수 있는 신뢰할 수 있는 유형을 생성합니다. 예를 들어 DOMPurify 를 사용하여 HTML 스니펫을 삭제하고 XSS 페이로드를 삭제할 수 있습니다.
import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(html, {RETURN_TRUSTED_TYPE: true});
DOMPurify 신뢰할 수 있는 유형을 지원하고
브라우저에서 위반을 생성하지 않도록 TrustedHTML 객체로 래핑된 삭제된 HTML을 반환합니다.
신뢰할 수 있는 유형 정책 만들기
위반을 일으키는 코드를 삭제할 수 없고 값을 삭제하고 신뢰할 수 있는 유형을 만드는 라이브러리가 없는 경우도 있습니다. 이러한 경우 신뢰할 수 있는 유형 객체를 직접 만들 수 있습니다.
먼저 정책을 만듭니다. 정책은 입력에 특정 보안 규칙을 적용하는 신뢰할 수 있는 유형의 팩터리입니다.
if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
const escapeHTMLPolicy = trustedTypes.createPolicy('myEscapePolicy', {
createHTML: string => string.replace(/\</g, '<')
});
}
이 코드는 createHTML() 함수를 사용하여 TrustedHTML 객체를 생성할 수 있는 myEscapePolicy라는 정책을 만듭니다. 정의된 규칙은 새 HTML 요소가 생성되지 않도록 < 문자를 HTML 이스케이프합니다.
다음과 같이 정책을 사용합니다.
const escaped = escapeHTMLPolicy.createHTML('<img src=x onerror=alert(1)>');
console.log(escaped instanceof TrustedHTML); // true
el.innerHTML = escaped; // '<img src=x onerror=alert(1)>'
기본 정책 사용
CDN에서 서드 파티 라이브러리를 로드하는 경우와 같이 위반 코드를 변경할 수 없는 경우도 있습니다. 이 경우 기본 정책을 사용합니다.
if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
trustedTypes.createPolicy('default', {
createHTML: (string, sink) => DOMPurify.sanitize(string, {RETURN_TRUSTED_TYPE: true})
});
}
default라는 정책은 신뢰할 수 있는 유형만 허용하는 싱크에서 문자열이 사용되는 모든 곳에서 사용됩니다.
콘텐츠 보안 정책 적용으로 전환
애플리케이션에서 더 이상 위반이 발생하지 않으면 신뢰할 수 있는 유형을 적용하기 시작할 수 있습니다.
Content-Security-Policy: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
이제 웹 애플리케이션이 아무리 복잡하더라도 DOM XSS 취약점을 도입할 수 있는 유일한 것은 정책 중 하나의 코드이며 정책 생성을 제한하여 이를 더욱 강화할 수 있습니다. 정책 생성을 제한하여