신뢰할 수 있는 유형으로 DOM 기반 교차 사이트 스크립팅 취약점 방지

애플리케이션의 DOM XSS 공격 표면을 줄입니다.

크르지스토프 코토비츠
크르지츠토프 코토비츠

Shorts가 중요한 이유

DOM 기반 교차 사이트 스크립팅 (DOM XSS)은 가장 일반적인 웹 보안 취약점 중 하나이며 애플리케이션에 도입하기가 매우 쉽습니다. 신뢰할 수 있는 유형은 기본적으로 위험한 웹 API 함수를 안전하게 보호하므로 DOM XSS 취약점이 없는 애플리케이션을 작성, 보안 검토, 유지관리할 수 있는 도구를 제공합니다. 신뢰할 수 있는 유형은 Chrome 83에서 지원되며 다른 브라우저에서는 polyfill을 사용할 수 있습니다. 최신 브라우저 간 지원 정보는 브라우저 호환성을 참고하세요.

배경

수년 동안 DOM XSS는 가장 만연하고 위험한 웹 보안 취약점 중 하나였습니다.

교차 사이트 스크립팅에는 두 가지 서로 다른 그룹이 있습니다. 일부 XSS 취약점은 웹사이트를 구성하는 HTML 코드를 안전하지 않게 만드는 서버 측 코드로 인해 발생합니다. 다른 경우에는 자바스크립트 코드가 사용자 제어 콘텐츠로 위험한 함수를 호출하는 근본 원인이 클라이언트에 있습니다.

서버 측 XSS를 방지하려면 문자열을 연결하여 HTML을 생성하지 말고 안전한 컨텍스트 자동 이스케이프 처리 템플릿 라이브러리를 대신 사용하세요. nonce 기반의 콘텐츠 보안 정책을 사용하여 불가피하게 발생하는 버그를 추가로 완화합니다.

이제 브라우저가 신뢰할 수 있는 유형을 사용하여 클라이언트 측 (DOM 기반이라고도 함) XSS를 방지할 수 있습니다.

API 소개

신뢰할 수 있는 유형은 다음과 같은 위험한 싱크 함수를 잠그는 방식으로 작동합니다. 브라우저 공급업체와 웹 프레임워크에서는 보안상의 이유로 이러한 기능을 사용하지 않도록 조치하고 있으므로 이미 알고 계실 수도 있습니다.

신뢰할 수 있는 유형을 사용하려면 데이터를 위의 싱크 함수에 전달하기 전에 처리해야 합니다. 문자열만 사용하면 브라우저가 데이터를 신뢰할 수 있는지 알 수 없으므로 실패합니다.

금지사항
anElement.innerHTML  = location.href;
신뢰할 수 있는 유형을 사용 설정하면 브라우저에서 TypeError가 발생하고 문자열과 함께 DOM XSS 싱크가 사용되지 않습니다.

데이터가 안전하게 처리되었음을 나타내기 위해 특수한 객체인 신뢰할 수 있는 유형을 만듭니다.

의견을 제시하지
anElement.innerHTML = aTrustedHTML;
신뢰할 수 있는 유형을 사용 설정하면 브라우저에서 HTML 스니펫을 예상하는 싱크에 TrustedHTML 객체를 허용합니다. 다른 민감한 싱크를 위한 TrustedScriptTrustedScriptURL 객체도 있습니다.

신뢰할 수 있는 유형은 애플리케이션의 DOM XSS 공격 표면을 크게 줄여줍니다. 보안 검토를 간소화하고, 브라우저에서 런타임 시 코드를 컴파일, 린트 또는 번들로 묶을 때 실행되는 유형 기반 보안 검사를 시행할 수 있습니다.

신뢰할 수 있는 유형 사용 방법

콘텐츠 보안 정책 위반 보고서 준비

보고서 수집기(예: 오픈소스 go-csp-collector)를 배포하거나 상용화되는 것 중 하나를 사용할 수 있습니다. 브라우저에서 위반을 디버그할 수도 있습니다. js document.addEventListener('securitypolicyviolation', console.error.bind(console));

보고서 전용 CSP 헤더 추가

신뢰할 수 있는 유형으로 마이그레이션하려는 문서에 다음 HTTP 응답 헤더를 추가합니다. text 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에서 innerHTML<img src=x로 시작하는 문자열로 호출되었음을 나타냅니다. 이 정보는 코드에서 DOM XSS를 도입할 수 있고 변경해야 하는 부분을 좁히는 데 도움이 됩니다.

위반사항 수정

신뢰할 수 있는 유형 위반을 수정할 수 있는 몇 가지 옵션이 있습니다. 문제가 되는 코드를 삭제하거나 라이브러리를 사용하거나 신뢰할 수 있는 유형 정책을 생성하거나 최후의 수단으로 기본 정책을 만들 수 있습니다.

문제가 되는 코드를 다시 작성하세요.

준수하지 않는 기능이 더 이상 필요하지 않거나 오류가 발생하기 쉬운 함수를 사용하지 않고 최신 방식으로 재작성할 수 있습니다.

금지사항
el.innerHTML = '';
의견을 제시하지
el.textContent = '';
const img = document.createElement('img');
img.src = 'xyz.jpg';
el.appendChild(img);

라이브러리 사용

일부 라이브러리는 이미 싱크 함수에 전달할 수 있는 신뢰할 수 있는 유형을 생성합니다. 예를 들어 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, '&lt;')
  });
}

이 코드는 createHTML() 함수를 통해 TrustedHTML 객체를 생성할 수 있는 myEscapePolicy라는 정책을 만듭니다. 정의된 규칙은 < 문자를 HTML로 이스케이프 처리하여 새로운 HTML 요소가 생성되지 않도록 합니다.

다음과 같이 정책을 사용합니다.

const escaped = escapeHTMLPolicy.createHTML('<img src=x onerror=alert(1)>');
console.log(escaped instanceof TrustedHTML);  // true
el.innerHTML = escaped;  // '&lt;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 취약점을 야기할 수 있는 유일한 요소는 정책 중 하나에 있는 코드일 때입니다. 정책 생성을 제한하여 이를 더욱 제한할 수 있습니다.

추가 자료