1. 소개
이 절은 규범적인 내용이 아닙니다.
웹 애플리케이션은 종종 클라이언트 측에서 HTML 문자열을 다루어야 합니다. 예를 들어, 클라이언트 측 템플릿 솔루션의 일부로 사용하거나, 사용자 생성 콘텐츠를 렌더링할 때 등입니다. 이를
안전하게 처리하는 것은 어렵습니다.
문자열을 단순히 이어붙여 Element
의
innerHTML
에 넣는 순진한 방식은 다양한 예기치 못한 방식으로 JavaScript가 실행될 위험이 있습니다.
[DOMPURIFY]와 같은 라이브러리는 문자열을 삽입하기 전에 신중하게 파싱하고 정화하여, DOM을 생성한 뒤 허용 목록을 통해 멤버를 필터링함으로써 이러한 문제를 관리하려고 시도합니다. 하지만 웹에 노출된 파싱 API가 실제 브라우저가 "진짜" DOM에 문자열을 렌더링하는 방식과 항상 합리적으로 대응되지 않기 때문에, 이는 취약한 접근 방식임이 입증되었습니다. 또한 라이브러리는 브라우저의 변화하는 동작을 계속 따라가야 하며, 한때 안전했던 것이 새로운 플랫폼 수준의 기능으로 인해 위험이 될 수 있습니다.
브라우저는 언제 코드를 실행할지 꽤 잘 알고 있습니다. 브라우저가 임의의 문자열에서 안전하게 HTML을 렌더링할 수 있도록 가르침으로써 사용자 공간 라이브러리보다 더 나은 방법을 제공할 수 있으며, 브라우저의 파서 구현이 변경될 때마다 함께 유지·업데이트될 가능성도 훨씬 높습니다. 이 문서는 바로 그 목적을 위한 API를 개략적으로 설명합니다.
1.1. 목표
-
개발자가 사용자 제어 HTML을 처리할 때, 주입 시 직접적인 스크립트 실행을 방지할 수 있는 메커니즘을 제공하여 DOM 기반 교차 사이트 스크립팅 공격의 위험을 줄입니다.
-
현재 사용자 에이전트의 HTML 이해도를 고려하여 HTML 출력을 안전하게 사용할 수 있도록 만듭니다.
-
개발자가 기본 요소 및 속성 집합을 재정의할 수 있도록 허용합니다. 특정 요소와 속성을 추가하면 스크립트 가젯 공격을 방지할 수 있습니다.
1.2. API 요약
Sanitizer API는 HTML이 포함된 문자열을 파싱하여 DOM 트리로 변환하고, 사용자 정의 설정에 따라 결과 트리를 필터링하는 기능을 제공합니다. 메서드는 두 가지 방식으로 나뉩니다.
-
안전 및 비안전: "안전" 메서드는 스크립트를 실행할 수 있는 마크업을 생성하지 않습니다. 즉, XSS로부터 안전해야 합니다. "비안전" 메서드는 주어진 대로 파싱하고 필터링합니다. 참고: § 4 보안 고려 사항.
-
컨텍스트: 메서드는
Element
와ShadowRoot
에 정의되며, 해당Node
의 자식을 대체합니다. 이는innerHTML
과 유사합니다. 또한Document
에도 정적 메서드가 있으며, 전체 문서를 파싱하며DOMParser
.parseFromString()
과 유사합니다.
2. 프레임워크
2.1. Sanitizer API
Element
인터페이스는 setHTML()
와
setHTMLUnsafe()
두 개의 메서드를 정의합니다.
두 메서드 모두 HTML 마크업이 담긴 DOMString
과 선택적 설정값을 받습니다.
partial interface Element { [CEReactions ]undefined ((
setHTMLUnsafe TrustedHTML or DOMString ),
html optional SetHTMLUnsafeOptions = {}); [
options CEReactions ]undefined (
setHTML DOMString ,
html optional SetHTMLOptions = {}); };
options
Element
의
setHTMLUnsafe(html, options) 메서드 단계:
-
compliantHTML을 Get Trusted Type compliant string 알고리즘에
TrustedHTML
, this의 relevant global object, html, "Element setHTMLUnsafe", 그리고 "script"를 넘겨 호출한 결과로 설정합니다. -
target을 this가 template contents를 가지면 해당 값으로, 아니면 this로 설정합니다.
-
Set and filter HTML을 target, this, compliantHTML, options, 그리고 false를 넘겨 호출합니다.
Element
의
setHTML(html, options) 메서드 단계:
-
target을 this가 template contents를 가지면 해당 값으로, 아니면 this로 설정합니다.
-
Set and filter HTML을 target, this, html, options, 그리고 true를 넘겨 호출합니다.
partial interface ShadowRoot { [CEReactions ]undefined ((
setHTMLUnsafe TrustedHTML or DOMString ),
html optional SetHTMLUnsafeOptions = {}); [
options CEReactions ]undefined (
setHTML DOMString ,
html optional SetHTMLOptions = {}); };
options
이 메서드들은 ShadowRoot
에도
동일하게 구현되어 있습니다:
ShadowRoot
의
setHTMLUnsafe(html, options) 메서드 단계:
-
compliantHTML을 Get Trusted Type compliant string 알고리즘에
TrustedHTML
, this의 relevant global object, html, "ShadowRoot setHTMLUnsafe", 그리고 "script"를 넘겨 호출한 결과로 설정합니다. -
Set and filter HTML을 this, this의 shadow host (컨텍스트 요소), compliantHTML, options, 그리고 false를 넘겨 호출합니다.
ShadowRoot
의
setHTML(html, options) 메서드 단계:
-
Set and filter HTML을 this (타깃), this (컨텍스트 요소), html, options, 그리고 true를 넘겨 호출합니다.
Document
인터페이스에는 전체 Document
를
파싱하는 새로운 메서드 두 개가 추가됩니다:
partial interface Document {static Document ((
parseHTMLUnsafe TrustedHTML or DOMString ),
html optional SetHTMLUnsafeOptions = {});
options static Document (
parseHTML DOMString ,
html optional SetHTMLOptions = {}); };
options
-
compliantHTML을 Get Trusted Type compliant string 알고리즘에
TrustedHTML
, this의 relevant global object, html, "Document parseHTMLUnsafe", 그리고 "script"를 넘겨 호출한 결과로 설정합니다. -
document를 새로운
Document
로, content type이 "text/html"로 설정합니다.참고: document에는 브라우징 컨텍스트가 없으므로 스크립팅이 비활성화되어 있습니다.
-
document의 allow declarative shadow roots를 true로 설정합니다.
-
Parse HTML from a string을 document와 compliantHTML로 호출합니다.
-
sanitizer를 get a sanitizer instance from options에 options와 false를 넘겨 호출한 결과로 설정합니다.
-
sanitize를 document, sanitizer, 그리고 false로 호출합니다.
-
document를 반환합니다.
-
document를 새로운
Document
로, content type이 "text/html"로 설정합니다.참고: document에는 브라우징 컨텍스트가 없으므로 스크립팅이 비활성화되어 있습니다.
-
document의 allow declarative shadow roots를 true로 설정합니다.
-
Parse HTML from a string을 document와 html로 호출합니다.
-
sanitizer를 get a sanitizer instance from options에 options와 true를 넘겨 호출한 결과로 설정합니다.
-
sanitize를 document, sanitizer, 그리고 true로 호출합니다.
-
document를 반환합니다.
2.2. SetHTML 옵션과 설정 객체
setHTML()
류의
메서드들은 모두 옵션 딕셔너리를 받습니다. 현재 이 딕셔너리에는 한 가지 멤버만 정의되어 있습니다:
enum {
SanitizerPresets };
"default" dictionary { (
SetHTMLOptions Sanitizer or SanitizerConfig or SanitizerPresets )= "default"; };
sanitizer dictionary { (
SetHTMLUnsafeOptions Sanitizer or SanitizerConfig or SanitizerPresets )= {}; };
sanitizer
Sanitizer
설정 객체는 필터 설정을 캡슐화합니다.
동일한 설정은 "안전" 또는 "비안전" 메서드
모두에 사용할 수 있으며, "안전" 메서드는 전달된 설정에 대해 암묵적으로
removeUnsafe
연산을 수행하며, 설정이 없을 경우 기본 설정을 사용합니다. 의도는
페이지 생명주기 초기에 하나 또는 소수의 설정을 미리 구축해두고 필요할 때마다 재사용하는 것입니다. 이렇게 하면 구현체가 설정을 미리 처리할 수 있습니다.
설정 객체는 설정 딕셔너리를 반환하도록 질의할 수 있으며, 직접 수정할 수도 있습니다.
[Exposed =Window ]interface {
Sanitizer (
constructor optional (SanitizerConfig or SanitizerPresets )= "default"); // 설정 질의:
configuration SanitizerConfig (); // Sanitizer의 리스트와 필드 수정:
get undefined (
allowElement SanitizerElementWithAttributes );
element undefined (
removeElement SanitizerElement );
element undefined (
replaceElementWithChildren SanitizerElement );
element undefined (
allowAttribute SanitizerAttribute );
attribute undefined (
removeAttribute SanitizerAttribute );
attribute undefined (
setComments boolean );
allow undefined (
setDataAttributes boolean ); // 스크립트 실행 마크업 제거. 여러 리스트를 수정할 수 있음:
allow undefined (); };
removeUnsafe
Sanitizer
는 configuration(SanitizerConfig
)을
가집니다.
-
만약 configuration이
SanitizerPresets
문자열이라면:-
configuration을 내장 안전 기본 설정으로 설정합니다.
-
valid를 set a configuration에 configuration과 true를 넘겨, this에서 호출한 결과로 설정합니다.
-
만약 valid가 false라면
TypeError
를 던집니다.
2.3. 설정 딕셔너리
dictionary {
SanitizerElementNamespace required DOMString ;
name DOMString ?= "http://www.w3.org/1999/xhtml"; }; // "elements"에서 사용됨
_namespace dictionary :
SanitizerElementNamespaceWithAttributes SanitizerElementNamespace {sequence <SanitizerAttribute >;
attributes sequence <SanitizerAttribute >; };
removeAttributes typedef (DOMString or SanitizerElementNamespace );
SanitizerElement typedef (DOMString or SanitizerElementNamespaceWithAttributes );
SanitizerElementWithAttributes dictionary {
SanitizerAttributeNamespace required DOMString ;
name DOMString ?=
_namespace null ; };typedef (DOMString or SanitizerAttributeNamespace );
SanitizerAttribute dictionary {
SanitizerConfig sequence <SanitizerElementWithAttributes >;
elements sequence <SanitizerElement >;
removeElements sequence <SanitizerElement >;
replaceWithChildrenElements sequence <SanitizerAttribute >;
attributes sequence <SanitizerAttribute >;
removeAttributes boolean ;
comments boolean ; };
dataAttributes
3. 알고리즘
Element
또는 DocumentFragment
target, Element
contextElement, 문자열 html, 딕셔너리
options, boolean safe이 주어졌을 때:
-
safe가 참이고 contextElement의 local name이 "
script
"이며 contextElement의 namespace가 HTML 네임스페이스 또는 SVG 네임스페이스라면, 반환한다. -
sanitizer를 옵션에서 sanitizer 인스턴스 얻기를 options와 safe로 호출한 결과로 설정한다.
-
newChildren을 HTML 프래그먼트 파싱 알고리즘에 contextElement, html, 그리고 true를 넘겨 호출한 결과로 설정한다.
-
fragment를 새로운
DocumentFragment
로, node document가 contextElement의 node document인 것으로 생성한다. -
각 node를 newChildren에서 fragment에 추가한다.
-
sanitize를 fragment에 sanitizer, safe로 호출한다.
-
모두 대체를 fragment로 target 내에서 실행한다.
참고: 이 알고리즘은 SetHTMLOptions
와
SetHTMLUnsafeOptions
모두에 적용된다.
기본값만 다르다.
3.1. 정화 알고리즘
ParentNode
node, Sanitizer
sanitizer, boolean safe를 사용하여 다음 단계를 실행한다:
-
configuration을 sanitizer의 configuration 값으로 설정한다.
-
safe가 true라면, configuration을 remove unsafe를 configuration에 호출한 결과로 설정한다.
-
sanitize core를 node, configuration, handleJavascriptNavigationUrls를 safe로 하여 호출한다.
ParentNode
node, SanitizerConfig
configuration,
boolean
handleJavascriptNavigationUrls를 사용하며,
DOM 트리를 node에서 시작하여 순회하고, 일부 특수한 경우(예: template contents)를 재귀적으로 처리할 수 있다. 단계는 다음과 같다:
-
각 child를 node의 children에서 반복한다:
-
Assert: child가 구현하는 것이
Text
,Comment
,Element
, 또는DocumentType
이어야 한다.참고: 현재 이 알고리즘은 HTML 파서의 결과에만 호출되며, 위의 assert가 항상 만족된다.
DocumentType
은parseHTML
과parseHTMLUnsafe
에서만 발생해야 한다. 만약 앞으로 이 알고리즘이 다른 문맥에서 사용된다면, 이 가정을 재검토해야 한다. -
child가 구현하는 것이
DocumentType
이면, continue한다. -
그 외에는:
-
elementName을
SanitizerElementNamespace
로, child의 local name과 namespace를 사용하여 만든다. -
configuration["
replaceWithChildrenElements
"] 가 elementName을 포함하면:-
sanitize core를 child, configuration, handleJavascriptNavigationUrls로 호출한다.
-
replace all을 child의 children에 대해 child 내에서 실행한다.
-
continue한다.
-
-
configuration["
removeElements
"] 가 elementName을 포함하거나, configuration["elements
"] 가 비어있지 않으면서 elementName을 포함하지 않으면: -
elementName이 «[ "
name
" → "template
", "namespace
" → HTML namespace ]»와 동일하면-
sanitize core를 child의 template contents와 configuration, handleJavascriptNavigationUrls로 호출한다.
-
-
child가 shadow host라면, sanitize core를 child의 shadow root와 configuration, handleJavascriptNavigationUrls로 호출한다.
-
각 attribute를 child의 attribute list에서 반복한다:
-
attrName을
SanitizerAttributeNamespace
로, attribute의 local name과 namespace로 만든다. -
configuration["
removeAttributes
"] 가 attrName을 포함하면, child에서 attribute를 제거한다. -
configuration["
elements
"]["removeAttributes
"] 가 attrName을 포함하면, child에서 attribute를 제거한다. -
다음 조건이 모두 false라면 child에서 attribute를 제거한다.
-
configuration["
attributes
"] 가 존재하고, attrName을 포함 한다 -
configuration["
elements
"]["attributes
"] 가 attrName을 포함 한다 -
"data-"가 코드 유닛 접두사로서 local name에 있고, namespace가
null
이며, configuration["dataAttributes
"] 가 true이다
-
-
handleJavascriptNavigationUrls가 참이라면:
-
«[elementName, attrName]»이 내장 네비게이팅 URL 속성 리스트에 일치하고, attribute가 javascript: URL을 포함하면, child에서 attribute를 제거한다.
-
child의 namespace가 MathML Namespace이고, attr의 local name이 "
href
"이며, attr의 namespace가null
또는 XLink namespace이고, attr이 javascript: URL을 포함하면, attr를 제거 한다. -
내장 애니메이팅 URL 속성 리스트 가 «[elementName, attrName]»을 포함하고, attr의 값이 "
href
" 또는 "xlink:href
"라면, attr를 제거 한다.
-
-
-
sanitize core를 child와 configuration, handleJavascriptNavigationUrls로 호출한다.
-
-
javascript:
URL을 오직 네비게이션 시에만 지원한다.
네비게이션 자체는 XSS 위협이 아니므로 javascript:
URL로의 네비게이션만 처리하며, 일반적인 네비게이션은 처리하지 않는다.
선언적 네비게이션은 몇 가지 범주로 나뉜다:
-
앵커 요소. (HTML 및 SVG 네임스페이스의
<a>
) -
폼 요소(폼 action을 통한 네비게이션).
-
[SVG11] 애니메이션.
처음 두 가지는 내장 네비게이팅 URL 속성 리스트에서 다뤄진다.
MathML의 경우, 이 명세에 "네임스페이스별 글로벌" 규칙이 없으므로 별도의 규칙으로 처리된다.
SVG 애니메이션의 경우는
내장 애니메이팅 URL 속성 리스트에서 다뤄진다. 하지만 SVG 애니메이션
요소의 해석이 최종 타겟에 의존하며, 정화 중에는 최종 타겟을 알 수 없으므로 sanitize 알고리즘은 href
속성의 모든 애니메이션을 차단한다.
3.2. 설정 처리
SanitizerConfig
configuration에 적용하려면, 다음을 수행한다:
-
element를 요소 속성 표준화를 element에 적용한 결과로 설정한다.
-
제거 element를 configuration["
removeElements
"]에서 제거한다. -
제거 element를 configuration["
replaceWithChildrenElements
"]에서 제거한다.
참고: allowElement 처리는 다른 메서드보다 복잡하다. 요소 허용 리스트에 요소별 허용/제거 속성 리스트가 있을 수 있다. 먼저 주어진 요소를 리스트에서 제거한 후 다시 추가하는데, 이는 병합이나 다른 방식이 아닌 전달된 리스트로 완전히 재설정하는 효과가 있다. 즉, 요소별 허용/제거 리스트는 전체로만 설정된다.
참고: 제거는 이름과 네임스페이스로 매칭된다. 속성이
추가된 요소를 추가하더라도 removeElements
와 replaceWithChildrenElements
리스트에서 동일 요소를 제거한다.
SanitizerConfig
configuration에서 제거하려면:
-
element를 요소 표준화를 element에 적용한 결과로 설정한다.
-
추가 element를 configuration["
removeElements
"]에 추가한다. -
제거 element를 configuration["
replaceWithChildrenElements
"]에서 제거한다.
SanitizerConfig
configuration에 적용하려면:
-
element를 요소 표준화를 element에 적용한 결과로 설정한다.
-
추가 element를 configuration["
replaceWithChildrenElements
"]에 추가한다. -
제거 element를 configuration["
removeElements
"]에서 제거한다.
SanitizerConfig
configuration에 적용하려면:
-
attribute를 속성 표준화를 attribute에 적용한 결과로 설정한다.
-
추가 attribute를 configuration["
attributes
"]에 추가한다. -
제거 attribute를 configuration["
removeAttributes
"]에서 제거한다.
SanitizerConfig
configuration에서 제거하려면:
-
attribute를 속성 표준화를 attribute에 적용한 결과로 설정한다.
-
추가 attribute를 configuration["
removeAttributes
"]에 추가한다. -
제거 attribute를 configuration["
attributes
"]에서 제거한다.
SanitizerConfig
configuration에 적용하려면:
-
configuration["
comments
"] 를 allow로 설정한다.
SanitizerConfig
configuration에 적용하려면:
-
configuration["
dataAttributes
"] 를 allow로 설정한다.
참고: 이 알고리즘은 remove unsafe라 불리지만, 이 명세에서 "unsafe"는 JavaScript가 문서에 삽입될 때 실행되는 콘텐츠를 의미한다. 즉, 이 메서드는 XSS 위험을 제거한다.
unsafe 제거를 configuration에서 수행하려면:
-
Assert: 내장 안전 기준 설정에는
removeElements
와removeAttributes
키가 설정돼 있지만,elements
,replaceWithChildrenElements
,attributes
는 없다. -
result를 configuration의 복사본으로 설정한다.
-
각 element를 내장 안전 기준 설정[
removeElements
]에서 반복한다:-
요소 제거를 element, result에 대해 호출한다.
-
-
각 attribute를 내장 안전 기준 설정[
removeAttributes
]에서 반복한다:-
속성 제거를 attribute, result에 대해 호출한다.
-
-
각 attribute를 이벤트 핸들러 콘텐츠 속성에서 반복한다:
-
속성 제거를 attribute, result에 대해 호출한다.
-
-
result를 반환한다.
Sanitizer
sanitizer에 적용하려면:
-
각 element를 configuration["
elements
"] 에서 반복한다:-
요소 허용을 element, sanitizer의 configuration에 대해 호출한다.
-
-
각 element를 configuration["
removeElements
"] 에서 반복한다:-
요소 제거를 element, sanitizer의 configuration에 대해 호출한다.
-
-
각 element를 configuration["
replaceWithChildrenElements
"] 에서 반복한다:-
요소를 자식으로 대체를 element, sanitizer의 configuration에 대해 호출한다.
-
-
각 attribute를 configuration["
attributes
"] 에서 반복한다:-
속성 허용을 attribute, sanitizer의 configuration에 대해 호출한다.
-
-
각 attribute를 configuration["
removeAttributes
"] 에서 반복한다:-
속성 제거를 attribute, sanitizer의 configuration에 대해 호출한다.
-
-
configuration["
comments
"] 가 존재하면:-
주석 설정을 configuration["
comments
"] 과 sanitizer의 configuration에 대해 호출한다. -
그 외에는 주석 설정을 allowCommentsAndDataAttributes 와 sanitizer의 configuration에 대해 호출한다.
-
-
configuration["
dataAttributes
"] 가 존재하면:-
data 속성 설정을 configuration["
dataAttributes
"] 와 sanitizer의 configuration에 대해 호출한다. -
그 외에는 data 속성 설정을 allowCommentsAndDataAttributes 와 sanitizer의 configuration에 대해 호출한다.
-
-
다음 조건이 모두 true면 true를 반환한다:
-
크기가 configuration["
elements
"] 와 크기가 sanitizer의 configuration["elements
"]와 같다. -
크기가 configuration["
removeElements
"] 와 크기가 sanitizer의 configuration["removeElements
"]와 같다. -
크기가 configuration["
replaceWithChildrenElements
"] 와 크기가 sanitizer의 configuration["replaceWithChildrenElements
"]와 같다. -
크기가 configuration["
attributes
"] 와 크기가 sanitizer의 configuration["attributes
"]와 같다. -
크기가 configuration["
removeAttributes
"] 와 크기가 sanitizer의 configuration["removeAttributes
"]와 같다. -
configuration["
elements
"] 또는 configuration["removeElements
"] 가 존재하거나, 모두 존재하지 않지만 동시에 둘 다 존재해서는 안 된다. -
configuration["
attributes
"] 또는 configuration["removeAttributes
"] 가 존재하거나, 모두 존재하지 않지만 동시에 둘 다 존재해서는 안 된다.
-
참고: 이전 버전 명세에서는 설정 표준화가 복잡하게 정의돼 있었으나, 이제 각 메서드 정의에서 이 과정을 처리한다.
참고: 이 연산은 Sanitizer
조작 메서드로 정의된다. 이 메서드들은 다른 리스트에서 일치하는 항목을 제거한다.
마지막 단계의 크기 비교로 이를 확인한다.
예시: { allow: ["div", "div"] }
은 허용 리스트에 하나의 요소만 들어가게 되고, 최종 테스트에서 false가 되어 호출자가 예외를 던지게 된다.
SanitizerElementWithAttributes
element에 대해 다음을 수행한다:
-
result를 요소 표준화를 element에 적용한 결과로 설정한다.
-
element가 딕셔너리라면:
-
각 attribute를 element["
attributes
"]에서 반복한다:-
추가 속성 표준화를 attribute에 적용한 결과를 result["
attributes
"]에 추가한다.
-
-
각 attribute를 element["
removeAttributes
"]에서 반복한다:-
추가 속성 표준화를 attribute에 적용한 결과를 result["
removeAttributes
"]에 추가한다.
-
-
-
result를 반환한다.
SanitizerAttribute
attribute에 대해,
이름
표준화를 attribute와 null을 기본 네임스페이스로 하여 적용한 결과를 반환한다.
3.3. 지원 알고리즘
이 명세에서 사용하는 정규화된
요소
및 속성 이름
리스트의
멤버십은 "name
"과 "namespace
"
항목이 모두 일치하는지에 기반한다:
3.4. 내장 기능
내장 기능은 4가지가 있다:
내장 안전 기본 설정은 다음과 같다:
{ "elements" : [ { "name" : "html" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "head" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "title" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "body" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "article" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "section" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "nav" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "aside" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "h1" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "h2" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "h3" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "h4" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "h5" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "h6" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "hgroup" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "header" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "footer" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "address" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "p" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "hr" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "pre" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "blockquote" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "cite" , "namespace" : null } ] }, { "name" : "ol" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "reversed" , "namespace" : null }, { "name" : "start" , "namespace" : null }, { "name" : "type" , "namespace" : null } ] }, { "name" : "ul" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "menu" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "li" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "value" , "namespace" : null } ] }, { "name" : "dl" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "dt" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "dd" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "figure" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "figcaption" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "main" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "search" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "div" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "a" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "href" , "namespace" : null }, { "name" : "rel" , "namespace" : null }, { "name" : "hreflang" , "namespace" : null }, { "name" : "type" , "namespace" : null } ] }, { "name" : "em" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "strong" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "small" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "s" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "cite" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "q" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "dfn" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "abbr" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "ruby" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "rt" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "rp" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "data" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "value" , "namespace" : null } ] }, { "name" : "time" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "datetime" , "namespace" : null } ] }, { "name" : "code" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "var" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "samp" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "kbd" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "sub" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "sup" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "i" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "b" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "u" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "mark" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "bdi" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "bdo" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "span" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "br" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "wbr" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "ins" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "cite" , "namespace" : null }, { "name" : "datetime" , "namespace" : null } ] }, { "name" : "del" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "cite" , "namespace" : null }, { "name" : "datetime" , "namespace" : null } ] }, { "name" : "table" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "caption" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "colgroup" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "span" , "namespace" : null } ] }, { "name" : "col" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "span" , "namespace" : null } ] }, { "name" : "tbody" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "thead" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "tfoot" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "tr" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "td" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "colspan" , "namespace" : null }, { "name" : "rowspan" , "namespace" : null }, { "name" : "headers" , "namespace" : null } ] }, { "name" : "th" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "colspan" , "namespace" : null }, { "name" : "rowspan" , "namespace" : null }, { "name" : "headers" , "namespace" : null }, { "name" : "scope" , "namespace" : null }, { "name" : "abbr" , "namespace" : null } ] }, { "name" : "math" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "merror" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mfrac" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mi" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mmultiscripts" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mn" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mo" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [ { "name" : "form" , "namespace" : null }, { "name" : "fence" , "namespace" : null }, { "name" : "separator" , "namespace" : null }, { "name" : "lspace" , "namespace" : null }, { "name" : "rspace" , "namespace" : null }, { "name" : "stretchy" , "namespace" : null }, { "name" : "symmetric" , "namespace" : null }, { "name" : "maxsize" , "namespace" : null }, { "name" : "minsize" , "namespace" : null }, { "name" : "largeop" , "namespace" : null }, { "name" : "movablelimits" , "namespace" : null } ] }, { "name" : "mover" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [ { "name" : "accent" , "namespace" : null } ] }, { "name" : "mpadded" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [ { "name" : "width" , "namespace" : null }, { "name" : "height" , "namespace" : null }, { "name" : "depth" , "namespace" : null }, { "name" : "lspace" , "namespace" : null }, { "name" : "voffset" , "namespace" : null } ] }, { "name" : "mphantom" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mprescripts" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mroot" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mrow" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "ms" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mspace" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [ { "name" : "width" , "namespace" : null }, { "name" : "height" , "namespace" : null }, { "name" : "depth" , "namespace" : null } ] }, { "name" : "msqrt" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mstyle" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "msub" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "msubsup" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "msup" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mtable" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mtd" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [ { "name" : "columnspan" , "namespace" : null }, { "name" : "rowspan" , "namespace" : null } ] }, { "name" : "mtext" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mtr" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "munder" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [ { "name" : "accentunder" , "namespace" : null } ] }, { "name" : "munderover" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [ { "name" : "accent" , "namespace" : null }, { "name" : "accentunder" , "namespace" : null } ] }, { "name" : "semantics" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "svg" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "viewBox" , "namespace" : null }, { "name" : "preserveAspectRatio" , "namespace" : null }, { "name" : "height" , "namespace" : null }, { "name" : "width" , "namespace" : null }, { "name" : "x" , "namespace" : null }, { "name" : "y" , "namespace" : null } ] }, { "name" : "g" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [] }, { "name" : "defs" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [] }, { "name" : "title" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [] }, { "name" : "desc" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [] }, { "name" : "metadata" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [] }, { "name" : "path" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "pathLength" , "namespace" : null }, { "name" : "d" , "namespace" : null } ] }, { "name" : "rect" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "pathLength" , "namespace" : null }, { "name" : "x" , "namespace" : null }, { "name" : "y" , "namespace" : null }, { "name" : "width" , "namespace" : null }, { "name" : "height" , "namespace" : null }, { "name" : "rx" , "namespace" : null }, { "name" : "ry" , "namespace" : null } ] }, { "name" : "circle" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "pathLength" , "namespace" : null }, { "name" : "cx" , "namespace" : null }, { "name" : "cy" , "namespace" : null }, { "name" : "r" , "namespace" : null } ] }, { "name" : "ellipse" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "pathLength" , "namespace" : null }, { "name" : "cx" , "namespace" : null }, { "name" : "cy" , "namespace" : null }, { "name" : "rx" , "namespace" : null }, { "name" : "ry" , "namespace" : null } ] }, { "name" : "line" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "pathLength" , "namespace" : null }, { "name" : "x1" , "namespace" : null }, { "name" : "y1" , "namespace" : null }, { "name" : "x2" , "namespace" : null }, { "name" : "y2" , "namespace" : null } ] }, { "name" : "polyline" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "pathLength" , "namespace" : null }, { "name" : "points" , "namespace" : null } ] }, { "name" : "polygon" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "pathLength" , "namespace" : null }, { "name" : "points" , "namespace" : null } ] }, { "name" : "text" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "lengthAdjust" , "namespace" : null }, { "name" : "x" , "namespace" : null }, { "name" : "y" , "namespace" : null }, { "name" : "dx" , "namespace" : null }, { "name" : "dy" , "namespace" : null }, { "name" : "rotate" , "namespace" : null }, { "name" : "textLength" , "namespace" : null } ] }, { "name" : "tspan" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "lengthAdjust" , "namespace" : null }, { "name" : "x" , "namespace" : null }, { "name" : "y" , "namespace" : null }, { "name" : "dx" , "namespace" : null }, { "name" : "dy" , "namespace" : null }, { "name" : "rotate" , "namespace" : null }, { "name" : "textLength" , "namespace" : null } ] }, { "name" : "textPath" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "lengthAdjust" , "namespace" : null }, { "name" : "textLength" , "namespace" : null }, { "name" : "path" , "namespace" : null }, { "name" : "startOffset" , "namespace" : null }, { "name" : "method" , "namespace" : null }, { "name" : "spacing" , "namespace" : null }, { "name" : "side" , "namespace" : null } ] }, { "name" : "foreignObject" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "x" , "namespace" : null }, { "name" : "y" , "namespace" : null }, { "name" : "width" , "namespace" : null }, { "name" : "height" , "namespace" : null } ] }, { "name" : "marker" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "viewBox" , "namespace" : null }, { "name" : "preserveAspectRatio" , "namespace" : null }, { "name" : "refX" , "namespace" : null }, { "name" : "refY" , "namespace" : null }, { "name" : "markerUnits" , "namespace" : null }, { "name" : "markerWidth" , "namespace" : null }, { "name" : "markerHeight" , "namespace" : null }, { "name" : "orient" , "namespace" : null } ] } ], "attributes" : [ { "name" : "dir" , "namespace" : null }, { "name" : "lang" , "namespace" : null }, { "name" : "title" , "namespace" : null }, { "name" : "displaystyle" , "namespace" : null }, { "name" : "mathbackground" , "namespace" : null }, { "name" : "mathcolor" , "namespace" : null }, { "name" : "mathsize" , "namespace" : null }, { "name" : "scriptlevel" , "namespace" : null }, { "name" : "fill" , "namespace" : null }, { "name" : "transform" , "namespace" : null }, { "name" : "alignment-baseline" , "namespace" : null }, { "name" : "baseline-shift" , "namespace" : null }, { "name" : "clip-path" , "namespace" : null }, { "name" : "clip-rule" , "namespace" : null }, { "name" : "color" , "namespace" : null }, { "name" : "color-interpolation" , "namespace" : null }, { "name" : "cursor" , "namespace" : null }, { "name" : "direction" , "namespace" : null }, { "name" : "display" , "namespace" : null }, { "name" : "dominant-baseline" , "namespace" : null }, { "name" : "fill-opacity" , "namespace" : null }, { "name" : "fill-rule" , "namespace" : null }, { "name" : "font-family" , "namespace" : null }, { "name" : "font-size" , "namespace" : null }, { "name" : "font-size-adjust" , "namespace" : null }, { "name" : "font-stretch" , "namespace" : null }, { "name" : "font-style" , "namespace" : null }, { "name" : "font-variant" , "namespace" : null }, { "name" : "font-weight" , "namespace" : null }, { "name" : "letter-spacing" , "namespace" : null }, { "name" : "marker-end" , "namespace" : null }, { "name" : "marker-mid" , "namespace" : null }, { "name" : "marker-start" , "namespace" : null }, { "name" : "opacity" , "namespace" : null }, { "name" : "paint-order" , "namespace" : null }, { "name" : "pointer-events" , "namespace" : null }, { "name" : "shape-rendering" , "namespace" : null }, { "name" : "stop-color" , "namespace" : null }, { "name" : "stop-opacity" , "namespace" : null }, { "name" : "stroke" , "namespace" : null }, { "name" : "stroke-dasharray" , "namespace" : null }, { "name" : "stroke-dashoffset" , "namespace" : null }, { "name" : "stroke-linecap" , "namespace" : null }, { "name" : "stroke-linejoin" , "namespace" : null }, { "name" : "stroke-miterlimit" , "namespace" : null }, { "name" : "stroke-opacity" , "namespace" : null }, { "name" : "stroke-width" , "namespace" : null }, { "name" : "text-anchor" , "namespace" : null }, { "name" : "text-decoration" , "namespace" : null }, { "name" : "text-overflow" , "namespace" : null }, { "name" : "text-rendering" , "namespace" : null }, { "name" : "transform-origin" , "namespace" : null }, { "name" : "unicode-bidi" , "namespace" : null }, { "name" : "vector-effect" , "namespace" : null }, { "name" : "visibility" , "namespace" : null }, { "name" : "white-space" , "namespace" : null }, { "name" : "word-spacing" , "namespace" : null }, { "name" : "writing-mode" , "namespace" : null } ], "comments" : false , "dataAttributes" : false }
참고: 포함된 [MathML] 마크업은 [SafeMathML]을 기반으로 합니다.
내장 안전 기준 설정은 스크립트 콘텐츠만 차단하는 것을 목적으로 합니다. 다음과 같습니다:
{ "removeElements" : [ { "namespace" : "http://www.w3.org/1999/xhtml" , "name" : "script" }, { "namespace" : "http://www.w3.org/1999/xhtml" , "name" : "frame" }, { "namespace" : "http://www.w3.org/1999/xhtml" , "name" : "iframe" }, { "namespace" : "http://www.w3.org/1999/xhtml" , "name" : "object" }, { "namespace" : "http://www.w3.org/1999/xhtml" , "name" : "embed" }, { "namespace" : "http://www.w3.org/2000/svg" , "name" : "script" }, { "namespace" : "http://www.w3.org/2000/svg" , "name" : "use" } ], "removeAttributes" : [] }
경고: remove unsafe 알고리즘은 추가적으로 이벤트 핸들러 콘텐츠 속성을 제거하도록 지정합니다. 해당 속성은 [HTML]에서 정의됩니다. 만약 사용자 에이전트가 [HTML] 명세에 추가적인 이벤트 핸들러 콘텐츠 속성을 확장하여 정의한다면, 이에 대한 처리는 각 사용자 에이전트의 책임입니다. 현재 이벤트 핸들러 콘텐츠 속성 리스트를 사용할 경우, 안전 기준 설정은 실질적으로 다음과 같이 동작합니다:
{ "removeElements" : [ { "namespace" : "http://www.w3.org/1999/xhtml" , "name" : "script" }, { "namespace" : "http://www.w3.org/1999/xhtml" , "name" : "frame" }, { "namespace" : "http://www.w3.org/1999/xhtml" , "name" : "iframe" }, { "namespace" : "http://www.w3.org/1999/xhtml" , "name" : "object" }, { "namespace" : "http://www.w3.org/1999/xhtml" , "name" : "embed" }, { "namespace" : "http://www.w3.org/2000/svg" , "name" : "script" }, { "namespace" : "http://www.w3.org/2000/svg" , "name" : "use" } ], "removeAttributes" : [ "onafterprint" , "onauxclick" , "onbeforeinput" , "onbeforematch" , "onbeforeprint" , "onbeforeunload" , "onbeforetoggle" , "onblur" , "oncancel" , "oncanplay" , "oncanplaythrough" , "onchange" , "onclick" , "onclose" , "oncontextlost" , "oncontextmenu" , "oncontextrestored" , "oncopy" , "oncuechange" , "oncut" , "ondblclick" , "ondrag" , "ondragend" , "ondragenter" , "ondragleave" , "ondragover" , "ondragstart" , "ondrop" , "ondurationchange" , "onemptied" , "onended" , "onerror" , "onfocus" , "onformdata" , "onhashchange" , "oninput" , "oninvalid" , "onkeydown" , "onkeypress" , "onkeyup" , "onlanguagechange" , "onload" , "onloadeddata" , "onloadedmetadata" , "onloadstart" , "onmessage" , "onmessageerror" , "onmousedown" , "onmouseenter" , "onmouseleave" , "onmousemove" , "onmouseout" , "onmouseover" , "onmouseup" , "onoffline" , "ononline" , "onpagehide" , "onpagereveal" , "onpageshow" , "onpageswap" , "onpaste" , "onpause" , "onplay" , "onplaying" , "onpopstate" , "onprogress" , "onratechange" , "onreset" , "onresize" , "onrejectionhandled" , "onscroll" , "onscrollend" , "onsecuritypolicyviolation" , "onseeked" , "onseeking" , "onselect" , "onslotchange" , "onstalled" , "onstorage" , "onsubmit" , "onsuspend" , "ontimeupdate" , "ontoggle" , "onunhandledrejection" , "onunload" , "onvolumechange" , "onwaiting" , "onwheel" ] }
javascript:
" 네비게이션이 "안전하지 않음"으로 간주되는 내장 네비게이팅 URL 속성 리스트는 다음과 같습니다:
«[
[
{ "name
" → "a
", "namespace
" → HTML namespace
},
{ "name
" → "href
", "namespace
" → null
}
],
[
{ "name
" → "area
", "namespace
" → HTML namespace
},
{ "name
" → "href
", "namespace
" → null
}
],
[
{ "name
" → "base
", "namespace
" → HTML namespace
},
{ "name
" → "href
", "namespace
" → null
}
],
[
{ "name
" → "button
", "namespace
" → HTML namespace
},
{ "name
" → "formaction
", "namespace
" → null
}
],
[
{ "name
" → "form
", "namespace
" → HTML namespace
},
{ "name
" → "action
", "namespace
" → null
}
],
[
{ "name
" → "iframe
", "namespace
" → HTML namespace
},
{ "name
" → "src
", "namespace
" → null
}
],
[
{ "name
" → "input
", "namespace
" → HTML namespace
},
{ "name
" → "formaction
", "namespace
" → null
}
],
[
{ "name
" → "a
", "namespace
" → SVG namespace },
{ "name
" → "href
", "namespace
" → null
}
],
[
{ "name
" → "a
", "namespace
" → SVG namespace },
{ "name
" → "href
", "namespace
" → XLink
namespace }
],
]»
내장
애니메이팅 URL 속성 리스트는
[SVG11]에서 선언적으로 네비게이션 요소를
"javascript:
"
URL로 변경할 때 사용할 수 있으며, 다음과 같습니다:
«[
[
{ "name
" → "animate
", "namespace
" → SVG namespace },
{ "name
" → "attributeName
", "namespace
" → null
] }
],
[
{ "name
" → "animateMotion
", "namespace
" → SVG namespace },
{ "name
" → "attributeName
", "namespace
" → null
}
],
[
{ "name
" → "animateTransform
", "namespace
" → SVG namespace },
{ "name
" → "attributeName
", "namespace
" → null
}
],
[
{ "name
" → "set
", "namespace
" → SVG namespace },
{ "name
" → "attributeName
", "namespace
" → null
}
],
]»
4. 보안 고려사항
Sanitizer API는 DOM 기반 크로스사이트 스크립팅(XSS)을 방지하기 위해, 제공된 HTML 콘텐츠를 순회하며 설정에 따라 요소 및 속성을 제거합니다. 명세된 API는 스크립트 실행이 가능한 마크업이 남는 Sanitizer 객체의 생성을 지원해서는 안 되며, 그렇게 된다면 위협 모델의 버그입니다.
그렇다고 해도, Sanitizer API의 올바른 사용만으로는 보호할 수 없는 보안 이슈들이 있으며, 해당 시나리오들은 다음 절에서 설명합니다.
4.1. 서버 측 반사 및 저장 XSS
이 절은 규범적인 내용이 아닙니다.
Sanitizer API는 DOM 내에서만 동작하며, 기존 DocumentFragment를 순회하고 필터링하는 기능을 추가합니다. Sanitizer는 서버 측 반사 또는 저장 XSS를 다루지 않습니다.
4.2. DOM 클로버링
이 절은 규범적인 내용이 아닙니다.
DOM 클로버링은 악의적인 HTML이 id
또는 name
속성으로 요소를 명명하여, 응용 프로그램이 DOM의 HTML 요소의
children
과 같은 프로퍼티가 악성 콘텐츠에 의해 가려지는 공격을 의미합니다.
Sanitizer API는 기본 상태에서는 DOM 클로버링 공격을 방어하지 않지만, id
및 name
속성을 제거하도록 설정할 수 있습니다.
4.3. 스크립트 개짓을 이용한 XSS
이 절은 규범적인 내용이 아닙니다.
스크립트 개짓(script gadget)은 공격자가 인기 있는 자바스크립트 라이브러리의 기존 애플리케이션 코드를 활용해 자신의 코드를 실행시키는 기법입니다. 이는 무해해 보이는 코드나 실제로는 해롭지 않은 DOM 노드를 삽입하여, 프레임워크가 해당 입력을 해석해 자바스크립트를 실행하도록 하는 방식입니다.
Sanitizer API는 이러한 공격을 방지할 수 없으며, 페이지 작성자가 일반적으로 알 수 없는 요소를 명시적으로 허용해야 하며, 추가적으로 알 수 없는 속성, 요소 및
data-
, slot
속성, <slot>
, <template>
와 같이 템플릿 및 프레임워크
특화 코드를 명시적으로 설정해야 합니다. 이러한 제한이 충분하지 않을 수 있으므로, 페이지 작성자에게 서드파티 라이브러리의 동작을 주의 깊게 살필 것을 권장합니다.
4.4. 변형된 XSS(mXSS)
이 절은 규범적인 내용이 아닙니다.
변형된 XSS(mXSS)는 올바른 문맥 없이 HTML 조각을 파싱할 때 파서 문맥 불일치에 기반한 공격을 의미합니다. 특히, 파싱된 HTML 프래그먼트를 문자열로 직렬화한 뒤, 해당 문자열을 다른 부모 요소에 삽입할 때 파싱 및 해석 결과가 동일하다는 보장이 없습니다. 이런 공격의 예로는 외부 콘텐츠나 잘못 중첩된 태그의 파싱 동작 변화를 이용하는 방법이 있습니다.
Sanitizer API는 문자열을 노드 트리로 변환하는 기능만 제공합니다. 모든 sanitizer 함수는 암묵적으로 문맥을 전달합니다:
Element.setHTML()
은 현재 요소를 사용하고, Document.parseHTML()
은 새 문서를 생성합니다. 따라서 Sanitizer
API는 직접적으로 변형된 XSS의 영향을 받지 않습니다.
개발자가 정화된 노드 트리를 .innerHTML
등으로 문자열로 받아 다시 파싱한다면, 변형된 XSS가 발생할 수 있습니다. 이런 관행은 권장하지 않습니다. HTML을
문자열로 처리하거나 전달해야 한다면 해당 문자열은 신뢰하지 않아야 하며, DOM에 삽입할 때 반드시 다시 정화해야 합니다. 즉, 정화 후 직렬화된 HTML 트리는 더 이상 정화된 것으로 간주할
수 없습니다.
mXSS에 대한 더 자세한 설명은 [MXSS]를 참고하세요.
5. 감사의 말씀
이 작업은 cure53의 [DOMPURIFY],
Internet Explorer의 window.toStaticHTML()
및 Ben Bucksch의 원래
[HTMLSanitizer]에서 영감을
받았습니다.
Anne van Kesteren, Krzysztof Kotowicz, Tom Schuster, Luke Warlow,
Guillaume Weghsteen, Mike West에게 소중한 피드백을 주셔서 감사합니다.