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가
template
요소라면), 아니라면 this로 설정한다. -
Set and filter HTML을 target, this, compliantHTML, options, 그리고 false로 실행한다.
Element
의
setHTML(html, options)
메서드 단계는 다음과 같다:
-
target을 this의 template contents로 설정(만약 this가
template
라면), 아니라면 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)
메서드 단계는 다음과
같다:
-
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
parseHTMLUnsafe(html, options)
메서드 단계는 다음과 같다:
-
compliantHTML을 Get Trusted Type compliant string 알고리즘을
TrustedHTML
, this의 relevant global object, html, "Document parseHTMLUnsafe", 그리고 "script"와 함께 호출한 결과로 설정한다. -
document를 새로운
Document
로 설정하며, content type은 "text/html"로 한다.참고: document에 browsing context가 없으므로, 스크립트 실행이 비활성화된다.
-
document의 allow declarative shadow roots를 true로 설정한다.
-
HTML 문자열 파싱을 document와 compliantHTML로 수행한다.
-
sanitizer를 옵션에서 sanitizer 인스턴스 가져오기를 options와 false로 호출한 결과로 설정한다.
-
sanitize를 document와 sanitizer, false로 호출한다.
-
document를 반환한다.
parseHTML(html, options)
메서드 단계는
다음과 같다:
-
document를 새로운
Document
로 설정하며, content type은 "text/html"로 한다.참고: document에 browsing context가 없으므로, 스크립트 실행이 비활성화된다.
-
document의 allow declarative shadow roots를 true로 설정한다.
-
HTML 문자열 파싱을 document와 html로 수행한다.
-
sanitizer를 옵션에서 sanitizer 인스턴스 가져오기를 options와 true로 호출한 결과로 설정한다.
-
sanitize를 document와 sanitizer, true로 호출한다.
-
document를 반환한다.
2.2. SetHTML 옵션 및 구성 객체
setHTML()
계열의
메서드는 모두 options
딕셔너리를 인자로 받는다. 현재 이 딕셔너리에는 한 가지 멤버만 정의되어 있다:
enum {
SanitizerPresets };
"default" dictionary { (
SetHTMLOptions Sanitizer or SanitizerConfig or SanitizerPresets )= "default"; };
sanitizer dictionary { (
SetHTMLUnsafeOptions Sanitizer or SanitizerConfig or SanitizerPresets )= {}; };
sanitizer
Sanitizer
구성 객체는 필터 구성을 캡슐화한다.
동일한 구성은 "safe"
또는 "unsafe" 메서드 모두에서 사용할 수 있는데,
"safe" 메서드는 전달된 구성에 대해 암시적으로
removeUnsafe
연산을 수행하며, 별도의 구성이 없을 경우 기본값을 사용한다. 기본값은 "safe"와
"unsafe" 메서드 간에 다르다: "safe" 메서드는 기본적으로 안전을 지향하며 제한적인 기본값을, "unsafe" 메서드는 제한 없이 동작한다.
구성 객체는 페이지 수명 초기에 하나 또는 몇 개만 미리 만들어 두고, 필요할 때마다 재사용하는 것이 의도다.
이는 구현체가 구성을 사전 처리할 수 있게 해준다.
구성 객체는 쿼리하여 구성 딕셔너리를 반환할 수 있고, 직접 수정할 수도 있다.
[Exposed =Window ]interface {
Sanitizer constructor (optional (SanitizerConfig or SanitizerPresets )= "default"); // Query configuration:
configuration SanitizerConfig get (); // Modify a Sanitizer’s lists and fields:boolean allowElement (SanitizerElementWithAttributes );
element boolean removeElement (SanitizerElement );
element boolean replaceElementWithChildren (SanitizerElement );
element boolean allowAttribute (SanitizerAttribute );
attribute boolean removeAttribute (SanitizerAttribute );
attribute boolean setComments (boolean );
allow boolean setDataAttributes (boolean ); // Remove markup that executes script.
allow boolean removeUnsafe (); };
Sanitizer
인스턴스는 SanitizerConfig
구성(configuration)을 가진다.
constructor(configuration)
메서드 단계는 다음과 같다:
-
configuration이
SanitizerPresets
문자열인 경우:-
configuration을 내장 안전 기본 구성으로 설정한다.
-
valid를 구성 설정에 configuration과 true, 그리고 this를 넘겨 호출한 반환 값으로 설정한다.
-
valid가 false라면
TypeError
를 throw한다.
get()
메서드 단계는 다음과 같습니다:
-
config를 this의 configuration으로 설정한다.
-
-
모든 element에 대해 config["
elements
"]에서:-
만약 element["
attributes
"] 존재한다면:-
element["
attributes
"]를 오름차순 정렬의 결과로 설정한다. element["attributes
"], attrA가 less than item attrB일 때.
-
-
만약 element["
removeAttributes
"] 존재한다면:-
element["
removeAttributes
"]를 오름차순 정렬의 결과로 설정한다. element["removeAttributes
"], attrA가 less than item attrB일 때.
-
-
-
config["
elements
"]를 오름차순 정렬의 결과로 설정한다. config["elements
"], elementA가 less than item elementB일 때.
-
-
만약 config["
removeElements
"] 존재한다면:-
config["
removeElements
"]를 오름차순 정렬의 결과로 설정한다. config["removeElements
"], elementA가 less than item elementB일 때.
-
-
만약 config["
replaceWithChildrenElements
"] 존재한다면:-
config["
replaceWithChildrenElements
"]를 오름차순 정렬의 결과로 설정한다. config["replaceWithChildrenElements
"], elementA가 less than item elementB일 때.
-
-
만약 config["
attributes
"] 존재한다면:-
config["
attributes
"]를 오름차순 정렬의 결과로 설정한다. config["attributes
"], attrA가 less than item attrB일 때.
-
-
만약 config["
removeAttributes
"] 존재한다면:-
config["
removeAttributes
"]를 오름차순 정렬의 결과로 설정한다. config["removeAttributes
"], attrA가 less than item attrB일 때.
-
-
config를 반환한다.
removeUnsafe()
메서드 단계는
this의 구성(configuration)을 remove unsafe를
this의 구성(configuration)에 대해 호출한 결과로 갱신하는 것이다.
2.3. 구성 딕셔너리
dictionary {
SanitizerElementNamespace required DOMString ;
name DOMString ?= "http://www.w3.org/1999/xhtml"; }; // Used by "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
2.4. 구성 불변 조건
구성은 개발자가 자신의 목적에 맞게 수정할 수 있으며, 그래야 한다. 옵션으로는 새로운 구성 딕셔너리를 처음부터 작성하거나,
기존 Sanitizer
의
구성을 수정하는 방법,
또는 get()
을
통해 기존 Sanitizer
의
구성을 딕셔너리로 받아 수정한 후,
새 Sanitizer
로
생성할 수도 있다.
빈 구성은 모든 것을 허용한다("unsafe" 계열 메서드, 예를 들어
setHTMLUnsafe
를
사용할 때).
"default"
구성은 내장 안전 기본 구성을 가진다.
"safe"와 "unsafe" sanitizer 메서드는 서로 다른 기본값을 가진다는 점에 유의해야 한다.
모든 구성 딕셔너리가 유효한 것은 아닙니다. 유효한 구성은 중복(같은 요소가 두 번 허용되는 경우 등)과 모순(같은 요소가 허용과 제거에 동시에 지정되는 경우 등)을 피해야 합니다.
구성이 유효하려면 여러 조건을 만족해야 합니다:
-
글로벌 허용/제거 리스트 혼합:
-
elements
또는removeElements
는 존재할 수 있지만, 둘 다 존재할 수는 없습니다. 둘 다 없으면, 이것은removeElements
를 « »로 설정한 것과 동일합니다. -
attributes
또는removeAttributes
는 존재할 수 있지만, 둘 다 존재할 수는 없습니다. 둘 다 없으면, 이것은removeAttributes
를 « »로 설정한 것과 동일합니다. -
dataAttributes
는 개념적으로attributes
허용 리스트의 확장입니다.dataAttributes
속성은attributes
리스트가 사용될 때만 허용됩니다.
-
-
다른 글로벌 리스트 간의 중복 항목:
-
elements
,removeElements
, 또는replaceWithChildrenElements
사이에는 중복 항목(즉, 같은 요소)이 없습니다. -
attributes
또는removeAttributes
사이에는 중복 항목(즉, 같은 속성)이 없습니다.
-
-
같은 요소에서의 중복 항목:
-
같은 요소에서
attributes
와removeAttributes
사이에는 중복 항목이 없습니다.
-
elements
요소 허용 리스트는 특정 요소에 대한 속성 허용/제거도 지정할 수 있습니다. 이는 [HTML]의 구조를 반영하기 위한 것으로,
글로벌 속성과
특정 요소에만 적용되는 로컬 속성을 모두 인지합니다.
글로벌과 로컬 속성은 혼합될 수 있지만, 특정 속성이 한 리스트에서는 허용되고 다른 리스트에서는 금지되는 모호한 구성은 일반적으로 허용되지 않습니다.
글로벌 attributes
| 글로벌 removeAttributes
| |
---|---|---|
로컬 attributes
| 속성이 두 리스트 중 하나에 일치하면 허용됩니다. 중복 항목은 허용되지 않습니다. | 속성이 로컬 허용 리스트에 있을 때만 허용됩니다. 글로벌 제거와 로컬 허용 리스트 사이에는 중복이 없어야 합니다. 글로벌 제거 리스트는 이 요소에서는 기능이 없지만, 로컬 허용 리스트가 없는 다른 요소에는 적용될 수 있습니다. |
로컬 removeAttributes
| 속성이 글로벌 허용 리스트에는 있고 로컬 제거 리스트에는 없으면 허용됩니다. 로컬 제거 리스트는 글로벌 허용 리스트의 부분집합이어야 합니다. | 속성이 어느 리스트에도 없으면 허용됩니다. 글로벌 제거와 로컬 제거 리스트 사이에는 중복이 없어야 합니다. |
글로벌과 per-element 리스트 간 중복이 대체로 허용되지 않지만, 글로벌 허용 리스트와 per-element 제거 리스트의 경우에는 후자가 전자의 부분집합이어야 하는 비대칭이 존재합니다. 위 표에서 중복만 집중한 발췌는 다음과 같습니다:
글로벌 attributes
| 글로벌 removeAttributes
| |
---|---|---|
로컬 attributes
| 중복 항목은 허용되지 않습니다. | 중복 항목은 허용되지 않습니다. |
로컬 removeAttributes
| 로컬 제거 리스트는 글로벌 허용 리스트의 부분집합이어야 합니다. | 중복 항목은 허용되지 않습니다. |
dataAttributes
설정은 커스텀 데이터 속성을 허용합니다. 위 규칙은
dataAttributes
를 허용 리스트로 간주하면 커스텀 데이터 속성에도 쉽게 확장됩니다:
글로벌 attributes
와 dataAttributes
설정
| |
---|---|
로컬 attributes
| 모든 커스텀 데이터 속성이 허용됩니다. 어떤 허용 리스트에도 커스텀 데이터 속성이 있으면 중복 항목이 됩니다. |
로컬 removeAttributes
| 커스텀 데이터 속성은 로컬 제거 리스트에 없으면 허용됩니다. 어떤 글로벌 허용 리스트에도 커스텀 데이터 속성이 있으면 중복 항목이 됩니다. |
이 규칙을 말로 표현하면 다음과 같습니다:
-
글로벌과 로컬 리스트 간의 중복 및 상호작용:
-
글로벌
attributes
허용 리스트가 존재하면, 모든 요소의 로컬 리스트에서:-
로컬
attributes
허용 리스트가 존재하면, 이들 리스트 간에는 중복 항목이 없어야 합니다. -
로컬
removeAttributes
제거 리스트가 존재하면, 그 모든 항목이 글로벌attributes
허용 리스트에도 있어야 합니다. -
dataAttributes
가 true이면, 어떤 허용 리스트에도 커스텀 데이터 속성이 있으면 안 됩니다.
-
-
글로벌
removeAttributes
제거 리스트가 존재하면:-
로컬
attributes
허용 리스트가 존재하면, 이들 리스트 간에는 중복 항목이 없어야 합니다. -
로컬
removeAttributes
제거 리스트가 존재하면, 이들 리스트 간에는 중복 항목이 없어야 합니다. -
dataAttributes
는 없어야 합니다.
-
-
SanitizerConfig
config가 유효(valid)하려면 다음 조건을 모두 만족해야 합니다:
-
config는
elements
또는removeElements
키를 가지며, 둘 다 갖지는 않습니다. -
config는
attributes
또는removeAttributes
키를 가지며, 둘 다 갖지는 않습니다. -
Assert: 모든
SanitizerElementNamespaceWithAttributes
,SanitizerElementNamespace
, 및SanitizerAttributeNamespace
항목들은 정규화(canonical)되어 있어야 하며, 각각 canonicalize a sanitizer element 또는 canonicalize a sanitizer attribute 를 거쳐야 합니다. -
config[
elements
], config[removeElements
], config[replaceWithChildrenElements
], config[attributes
], 또는 config[removeAttributes
] 가 존재한다면, 중복 항목이 없어야 합니다. -
config[
elements
] 와 config[replaceWithChildrenElements
] 가 존재한다면, config[elements
] 와 config[replaceWithChildrenElements
] 의 교집합은 빈 집합이어야 합니다. -
config[
removeElements
] 와 config[replaceWithChildrenElements
] 가 존재한다면, config[removeElements
] 와 config[replaceWithChildrenElements
] 의 교집합은 빈 집합이어야 합니다. -
config[
attributes
] 존재한다면:-
-
각 element에 대해 config[
elements
]:-
element[
attributes
] 와 element[removeAttributes
] 는 존재한다면 중복 항목이 없어야 합니다. -
config[
attributes
] 와 element[attributes
] 의 기본값 « »의 교집합은 빈 집합이어야 합니다. -
element[
removeAttributes
] 의 기본값 « »은 config[attributes
] 의 부분집합이어야 합니다. -
dataAttributes
가 존재하고,dataAttributes
가 true 이면:-
element[
attributes
] 에는 커스텀 데이터 속성이 없어야 합니다.
-
-
-
-
dataAttributes
가 true인 경우:-
config[
attributes
] 에 커스텀 데이터 속성이 없어야 합니다.
-
-
-
config[
removeAttributes
] 존재한다면:-
config[
elements
] 존재한다면, 각 element에 대해 config[elements
]:-
element[
attributes
] 와 element[removeAttributes
] 는 존재한다면 중복 항목이 없어야 합니다. -
config[
removeAttributes
] 와 element[attributes
] 의 기본값 « »의 교집합은 빈 집합이어야 합니다. -
config[
removeAttributes
] 와 element[removeAttributes
] 의 기본값 « »의 교집합은 빈 집합이어야 합니다.
-
-
config[
dataAttributes
] 는 존재하지 않아야 합니다.
-
참고: 구성 설정을 딕셔너리에서 할 때는 약간의 정규화(normalization)가 이루어집니다. 특히 허용/제거 리스트가 모두 없으면, 이것을 빈
제거 리스트로 해석합니다. 그래서 {}
자체는 유효(valid)한 구성은 아니지만,
{removeElements:[],removeAttributes:[]}
로 정규화되면 유효하게 됩니다. 이 정규화 과정은 비어있는 딕셔너리와 없는 딕셔너리를 일관되게
처리하기 위해 선택되었습니다. 즉, setHTMLUnsafe(txt)
와 setHTMLUnsafe(txt, {sanitizer: {}})
가 일관성을
갖도록 하기 위함입니다.
3. 알고리즘
Element
또는 DocumentFragment
target, Element
contextElement, string html, dictionary
options, 그리고 boolean safe가 주어집니다:
-
safe가 true이고 contextElement의 local name이 "
script
"이며, contextElement의 namespace가 HTML 네임스페이스 또는 SVG 네임스페이스라면, 반환합니다. -
sanitizer를 옵션에서 sanitizer 인스턴스 얻기를 options와 safe로 호출해서 얻습니다.
-
newChildren을 HTML 프래그먼트 파싱 알고리즘을 contextElement, html, true로 실행한 결과로 설정합니다.
-
fragment를 새
DocumentFragment
로 생성하고, 노드 document는 contextElement의 노드 document로 설정합니다. -
각 node를 newChildren에서 fragment에 append합니다.
-
sanitize를 fragment에 sanitizer와 safe로 실행합니다.
-
target 내 모든 자식을 fragment로 교체합니다.
참고: 이 알고리즘은 SetHTMLOptions
와
SetHTMLUnsafeOptions
모두에 적용됩니다.
두 옵션의 차이는 기본값뿐입니다.
-
sanitizerSpec를 "
default
"로 설정합니다. -
만약 options["
sanitizer
"] 가 존재하면:-
sanitizerSpec을 options["
sanitizer
"]로 설정합니다.
-
-
Assert: sanitizerSpec는
Sanitizer
인스턴스이거나, 문자열(string)로SanitizerPresets
멤버이거나, 딕셔너리여야 합니다. -
sanitizerSpec가 문자열이라면:
-
sanitizerSpec을 내장 안전 기본 구성으로 설정합니다.
-
sanitizerSpec이 딕셔너리라면:
-
sanitizer를 새
Sanitizer
인스턴스로 생성합니다. -
setConfigurationResult를 set a configuration 을 sanitizerSpec과 not safe로 sanitizer에 실행한 결과로 설정합니다.
-
setConfigurationResult가 false이면, TypeError를 throw합니다.
-
sanitizerSpec을 sanitizer로 설정합니다.
-
-
sanitizerSpec을 반환합니다.
3.1. Sanitize
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부터 재귀적으로 순회합니다. 절차는 다음과 같습니다:
-
각각 child에 대해 node의 children을 반복합니다:
-
단언: child가 구현함
Text
,Comment
,Element
, 또는DocumentType
.참고: 현재 이 알고리즘은 HTML 파서의 출력에만 호출되며, 이 단언이 반드시 만족되어야 합니다.
DocumentType
은parseHTML
및parseHTMLUnsafe
에서만 발생해야 합니다. 만약 앞으로 이 알고리즘이 다른 컨텍스트에서 사용된다면, 이 가정을 재검토해야 합니다. -
child가 DocumentType을 구현하면 continue합니다.
-
child가 Comment를 구현하는 경우:
-
그 외의 경우:
-
elementName을 child의 로컬 이름과 네임스페이스를 가진
SanitizerElementNamespace
로 설정한다. -
만약 configuration["
replaceWithChildrenElements
"] 존재하고 configuration["replaceWithChildrenElements
"] 포함하는 경우 elementName을:-
child에 대해 configuration과 handleJavascriptNavigationUrls를 사용하여 sanitize core를 호출한다.
-
계속 진행한다.
-
-
만약 configuration["
removeElements
"] 존재하고 configuration["removeElements
"] 포함하는 경우 elementName을: -
만약 configuration["
elements
"] 존재하고 configuration["elements
"]가 포함하지 않는 경우 elementName을: -
만약 elementName이 «[ "
name
" → "template
", "namespace
" → HTML namespace ]»와 같으면, child의 template contents에 대해 configuration과 handleJavascriptNavigationUrls를 사용하여 sanitize core를 호출한다. -
만약 child가 섀도우 호스트라면, child의 섀도우 루트에 대해 configuration과 handleJavascriptNavigationUrls를 사용하여 sanitize core를 호출한다.
-
elementWithLocalAttributes를 « [] »로 설정한다.
-
만약 configuration["
elements
"] 존재하고 configuration["elements
"] 포함하는 경우 elementName을:-
elementWithLocalAttributes를 configuration["
elements
"][elementName]로 설정한다.
-
-
각 attribute에 대해 child의 속성 리스트에서:
-
attrName을 attribute의 로컬 이름과 네임스페이스를 가진
SanitizerAttributeNamespace
로 설정한다. -
만약 elementWithLocalAttributes["
removeAttributes
"] 기본값 « »로 포함하는 경우 attrName을:-
제거 attribute.
-
-
그렇지 않고 configuration["
attributes
"] 존재하면:-
만약 configuration["
attributes
"]가 포함하지 않고 attrName을, elementWithLocalAttributes["attributes
"] 기본값 « »로 포함하지 않고 attrName을, 그리고 "data-"가 attribute의 로컬 이름의 코드 유닛 접두사가 아니고, 네임스페이스가null
이 아니거나 configuration["dataAttributes
"]가 true가 아니면:-
제거 attribute.
-
-
-
그 밖의 경우:
-
만약 elementWithLocalAttributes["
attributes
"] 존재하고 elementWithLocalAttributes["attributes
"]가 포함하지 않는 경우 attrName을:-
제거 attribute.
-
-
그렇지 않고 configuration["
removeAttributes
"] 포함하는 경우 attrName을:-
제거 attribute.
-
-
-
만약 handleJavascriptNavigationUrls이 true라면:
-
만약 «[elementName, attrName]»이 내장 내비게이팅 URL 속성 리스트에 일치하고, attribute가 javascript: URL을 포함한다면, 제거 attribute.
-
만약 child의 네임스페이스가 MathML 네임스페이스와 같고, attr의 로컬 이름이 "
href
"와 같고, attr의 네임스페이스가null
또는 XLink 네임스페이스이고, attr이 javascript: URL을 포함한다면, 제거 attribute. -
만약 내장 애니메이팅 URL 속성 리스트가 포함하는 경우 «[elementName, attrName]»을, 그리고 attr의 값이 "
href
" 또는 "xlink:href
"와 같으면, 제거 attribute.
-
-
-
child에 대해 configuration과 handleJavascriptNavigationUrls를 사용하여 sanitize core를 호출한다.
-
-
javascript:
URL을 navigation시에만 지원합니다.
navigation 자체는 XSS 위협이 아니므로, javascript:
URL로의 navigation만 처리하며 일반 navigation은 처리하지 않습니다.
선언적 navigation은 몇 가지 유형으로 나뉩니다:
-
앵커 요소 (
<a>
, HTML 및 SVG 네임스페이스) -
폼 요소 (폼 action으로 navigation을 트리거)
-
[SVG11] 애니메이션.
첫 두 경우는 내장 navigation URL 속성 리스트로 처리됩니다.
MathML의 경우는 별도 규칙으로 처리됩니다. 이 명세에 "네임스페이스 전역" 규칙이 없기 때문입니다.
SVG 애니메이션의 경우는 내장 애니메이션 URL 속성 리스트로 처리됩니다.
SVG 애니메이션 요소의 해석은 애니메이션의 대상에 따라 다르지만, sanitize 중에는 최종 대상 파악이 불가능하므로,
sanitize 알고리즘은 모든 애니메이션
href
속성을 차단합니다.
3.2. 구성 수정
구성 수정자 메서드는 Sanitizer
에 정의된
메서드로,
해당 인스턴스의 구성을 변경합니다.
이들은 유효성 기준을 유지합니다.
변경 여부를 호출자에게 알리는 boolean 값을 반환합니다.
let s= new Sanitizer({ elements: [ "div" ]}); s. allowElement( "p" ); // true를 반환합니다. div. setHTML( "<div><p>" , { sanitizer: s}); // `<div>` 및 `<p>`를 허용합니다.
let s= new Sanitizer({ elements: [ "div" ]}); s. removeElement( "p" ); // false를 반환합니다. <p>는 이전에 허용되지 않았습니다. div. setHTML( "<div><p>" , { sanitizer: s}); // `<div>`는 허용됩니다. `<p>`는 제거됩니다.
SanitizerElementWithAttributes
element을 SanitizerConfig
configuration과 함께 처리하려면:
-
글로벌 허용 리스트인지 제거 리스트인지 여부,
-
그리고 이러한 리스트가 이미 element를 포함하는지 여부.
-
element을 element로 속성을 포함한 sanitizer element 정규화의 결과로 설정한다.
-
만약 configuration["
elements
"] 존재하면:-
modified를 element를 configuration["
replaceWithChildrenElements
"] 에서 제거한 결과로 설정한다. -
코멘트: per-element 속성이 글로벌 속성과 겹치지 않도록 해야 한다.
-
만약 element["
attributes
"] 존재하면:-
element["
attributes
"] 를 element["attributes
"]에서 중복 제거한 결과로 설정한다. -
만약 configuration["
attributes
"] 존재하면:-
element["
attributes
"] 를 element["attributes
"] 와 configuration["attributes
"]의 차집합으로 설정한다. -
만약 configuration["
dataAttributes
"]가 true라면:-
element["
attributes
"] 에서 커스텀 데이터 속성인 item을 모두 제거한다.
-
-
-
만약 configuration["
removeAttributes
"] 존재하면:-
element["
attributes
"] 를 element["attributes
"] 와 configuration["removeAttributes
"]의 차집합으로 설정한다.
-
-
-
만약 element["
removeAttributes
"] 존재하면:-
element["
removeAttributes
"] 를 element["removeAttributes
"]에서 중복 제거한 결과로 설정한다. -
만약 configuration["
attributes
"] 존재하면:-
element["
removeAttributes
"] 를 element["removeAttributes
"] 와 configuration["attributes
"]의 교집합으로 설정한다.
-
-
만약 configuration["
removeAttributes
"] 존재하면:-
element["
removeAttributes
"] 를 element["removeAttributes
"] 와 configuration["removeAttributes
"]의 차집합으로 설정한다.
-
-
-
만약 configuration["
elements
"] 가 포함하지 않는 경우 element을: -
코멘트: 글로벌 허용 리스트에 이미 element가 있는 경우이다.
-
current element를 configuration["
elements
"]의 item 중 item[name
]이 element[name
]과 같고 item[namespace
]가 element[namespace
]와 같은 item으로 설정한다. -
만약 element가 current element와 같다면 modified를 반환한다.
-
true를 반환한다.
-
-
그 밖의 경우:
-
만약 element["
attributes
"] 존재하거나 element["removeAttributes
"] 기본값 « »이 빈 집합이 아니라면:-
이 작업이 지원되지 않음을 콘솔에 경고로 보고할 수 있다.
-
false를 반환한다.
-
-
modified를 element를 configuration["
replaceWithChildrenElements
"] 에서 제거한 결과로 설정한다. -
만약 configuration["
removeElements
"] 가 포함하지 않는 경우 element을:-
코멘트: 글로벌 제거 리스트에 element가 없는 경우이다.
-
modified를 반환한다.
-
-
코멘트: 글로벌 제거 리스트에 element가 있는 경우이다.
-
element를 configuration["
removeElements
"] 에서 제거한다. -
true를 반환한다.
-
SanitizerElement
element
을 SanitizerConfig
configuration에서 제거하려면:
-
글로벌 허용 목록(allow-list) 또는 제거 목록(remove-list)이 있는지,
-
그 목록에 이미 element가 포함되어 있는지 여부.
-
element를 sanitizer 요소 정규화의 결과로 설정한다.
-
modified를 element를 configuration["
replaceWithChildrenElements
"] 에서 제거한 결과로 설정한다. -
그 밖의 경우:
-
만약 configuration["
removeElements
"]가 포함하는 경우 element을:-
코멘트: 글로벌 제거 리스트에 이미 element가 있다.
-
modified를 반환한다.
-
-
코멘트: 글로벌 제거 리스트에 element가 없다.
-
element를 configuration["
removeElements
"]에 추가한다. -
true를 반환한다.
-
SanitizerElement
element를 SanitizerConfig
configuration에서:
-
element를 sanitizer 요소 정규화의 결과로 설정한다, 입력은 element.
-
만약 configuration["
replaceWithChildrenElements
"]가 포함한다면 element를:-
false를 반환한다.
-
-
Remove element from configuration["
removeElements
"]. -
Add element를 configuration["
replaceWithChildrenElements
"]에. -
true를 반환한다.
SanitizerAttribute
attribute를 SanitizerConfig
configuration에 대해:
참고: 이 메서드는 두 가지 경우를 구분한다. 즉, 글로벌 허용 리스트가 있는지 글로벌 제거 리스트가 있는지이다. 글로벌 허용 리스트에 attribute를 추가하면, 유효성 기준을 유지하기 위해 per-element 허용/제거 리스트를 보정하는 추가 작업이 필요할 수 있다.
-
attribute를 sanitizer 속성 정규화의 결과로 설정한다, 입력은 attribute.
-
만약 configuration["
attributes
"]가 존재한다면:-
주석: 글로벌 허용 리스트가 있다면, attribute를 추가해야 한다.
-
만약 configuration["
dataAttributes
"]가 true이고 attribute가 커스텀 데이터 속성이라면, false를 반환한다. -
만약 configuration["
attributes
"]가 포함한다면 attribute를 false를 반환한다. -
주석: per-element 허용/제거 리스트를 보정한다.
-
만약 configuration["
elements
"]가 존재한다면:-
각 element에 대해 configuration["
elements
"] 안의:-
만약 element["
attributes
"]가 기본값 « »로 포함한다면 attribute를:-
Remove attribute from element["
attributes
"].
-
-
Assert: element["
removeAttributes
"]가 기본값 « »일 때 포함하지 않는다 attribute를.
-
-
-
Append attribute to configuration["
attributes
"] -
true를 반환한다.
-
-
그렇지 않다면:
-
주석: 글로벌 제거 리스트가 있는 경우, attribute를 제거해야 한다.
-
만약 configuration["
removeAttributes
"]가 포함하지 않는다면 attribute를:-
false를 반환한다.
-
-
Remove attribute from configuration["
removeAttributes
"]. -
true를 반환한다.
-
SanitizerConfig
configuration에서:
참고: 이 메서드는 두 가지 경우를 구분한다. 즉, 글로벌 허용 리스트가 있는지 글로벌 제거 리스트가 있는지이다. 글로벌 제거 리스트에 attribute를 추가하면, 유효성 기준을 유지하기 위해 per-element 허용/제거 리스트를 보정하는 추가 작업이 필요할 수 있다. 글로벌 허용 리스트에서 attribute를 제거하는 경우, 로컬 제거 리스트에서도 이를 제거해야 할 수 있다.
-
attribute를 sanitizer 속성 정규화의 결과로 설정한다, 입력은 attribute.
-
만약 configuration["
attributes
"]가 존재한다면:-
주석: 글로벌 허용 리스트가 있는 경우, attribute를 추가해야 한다.
-
만약 configuration["
attributes
"]가 포함하지 않는다면 attribute를:-
false를 반환한다.
-
-
주석: per-element 허용/제거 리스트를 보정한다.
-
만약 configuration["
elements
"]가 존재한다면:-
각 element에 대해 configuration["
elements
"] 안의:-
만약 element["
removeAttributes
"]가 기본값 « »로 포함한다면 attribute를:-
Remove attribute from element["
removeAttributes
"].
-
-
-
-
Remove attribute from configuration["
attributes
"]. -
true를 반환한다.
-
-
그렇지 않다면:
-
주석: 글로벌 제거 리스트가 있는 경우, attribute를 추가해야 한다.
-
만약 configuration["
removeAttributes
"]가 포함한다면 attribute를 false를 반환한다. -
주석: per-element 허용/제거 리스트를 보정한다.
-
만약 configuration["
elements
"]가 존재한다면:-
각 element에 대해 configuration["
elements
"] 안의:-
만약 element["
attributes
"]가 기본값 « »로 포함한다면 attribute를:-
Remove attribute from element["
attributes
"].
-
-
만약 element["
removeAttributes
"]가 기본값 « »로 포함한다면 attribute를:-
Remove attribute from element["
removeAttributes
"].
-
-
-
-
Append attribute to configuration["
removeAttributes
"] -
true를 반환한다.
-
SanitizerConfig
configuration에서:
SanitizerConfig
configuration에서:
-
만약 configuration["
attributes
"]가 존재하지 않으면, false를 반환한다. -
만약 configuration["
dataAttributes
"]가 allow와 같다면, false를 반환한다. -
만약 allow가 true라면:
-
Remove 임의의 항목 attr를 configuration["
attributes
"]에서 제거하되, 그 attr가 커스텀 데이터 속성인 경우. -
만약 configuration["
elements
"]가 존재한다면:-
각 element에 대해 configuration["
elements
"] 안의:-
만약 element[
attributes
]가 존재한다면:-
Remove 임의의 항목 attr를 element[
attributes
]에서 제거하되, 그 attr가 커스텀 데이터 속성인 경우.
-
-
-
-
-
configuration["
dataAttributes
"]를 allow로 설정한다. -
true를 반환한다.
SanitizerConfig
configuration에서
수행하려면:
참고: 이 알고리즘의 이름은 remove unsafe이지만, 우리는 이 명세의 의미에서만 “unsafe”라는 용어를 사용하여, 문서에 삽입될 때 JavaScript가 실행되는 콘텐츠를 나타낸다. 즉, 이 메서드는 XSS의 기회를 제거한다.
-
Assert: 키 집합이 내장 안전 기준 구성의 동치는 «[ "
removeElements
", "removeAttributes
" ] »이다. -
result를 false로 둔다.
-
각 element에 대해 내장 안전 기준 구성[
removeElements
]의:-
요소 제거를 호출하여 element를 configuration에서 제거한다.
-
호출이 true를 반환했다면, result를 true로 설정한다.
-
-
각 attribute에 대해 내장 안전 기준 구성[
removeAttributes
]의:-
속성 제거를 호출하여 attribute를 configuration에서 제거한다.
-
호출이 true를 반환했다면, result를 true로 설정한다.
-
-
각 attribute에 대해, 이벤트 핸들러 콘텐츠 속성에 열거된:
-
속성 제거를 호출하여 attribute를 configuration에서 제거한다.
-
호출이 true를 반환했다면, result를 true로 설정한다.
-
-
result를 반환한다.
3.3. 구성 설정
Sanitizer
sanitizer:
-
구성 정규화(Canonicalize) configuration을 allowCommentsAndDataAttributes와 함께 수행합니다.
-
If configuration is not valid, then return false.
-
sanitizer의 configuration을 configuration으로 설정합니다.
-
True를 반환합니다.
3.4. 구성 정규화
Sanitizer
는 여러 처리 단계를 더 쉽게 하기 위해 configuration을 정규화된 형태로 저장합니다.
elements
목록 {elements: ["div"]}
은
{elements: [{name: "div", namespace: "http://www.w3.org/1999/xhtml"}]
}로 저장됩니다).
SanitizerConfig
configuration에
boolean
allowCommentsAndDataAttributes를 사용합니다:
참고: configuration은
JavaScript 값을 SanitizerConfig
로
변환한 [WebIDL]의 결과라고 가정합니다.
-
configuration["
elements
"]와 configuration["removeElements
"]가 모두 exist하지 않으면, set configuration["removeElements
"]를 « »로 설정합니다. -
configuration["
attributes
"]와 configuration["removeAttributes
"]가 모두 exist하지 않으면, set configuration["removeAttributes
"]를 « »로 설정합니다. -
configuration["
elements
"]가 exists인 경우:-
elements를 « »로 둡니다.
-
For each element of configuration["
elements
"] 에 대해 다음을 수행합니다:-
Append canonicalize a sanitizer element with attributes의 결과인 element를 elements에 추가합니다.
-
-
configuration["
elements
"]를 elements로 설정합니다.
-
-
configuration["
removeElements
"]가 exists인 경우:-
elements를 « »로 둡니다.
-
For each element of configuration["
removeElements
"] 에 대해 다음을 수행합니다:-
Append canonicalize a sanitizer element element의 결과를 elements에 추가합니다.
-
-
configuration["
removeElements
"]를 elements로 설정합니다.
-
-
configuration["
replaceWithChildrenElements
"]가 exists인 경우:-
elements를 « »로 둡니다.
-
For each element of configuration["
replaceWithChildrenElements
"] 에 대해 다음을 수행합니다:-
Append canonicalize a sanitizer element element의 결과를 elements에 추가합니다.
-
-
configuration["
replaceWithChildrenElements
"]를 elements로 설정합니다.
-
-
configuration["
attributes
"]가 exists인 경우:-
attributes를 « »로 둡니다.
-
For each attribute of configuration["
attributes
"] 에 대해 다음을 수행합니다:-
Append canonicalize a sanitizer attribute attribute의 결과를 attributes에 추가합니다.
-
-
configuration["
attributes
"]를 attributes로 설정합니다.
-
-
configuration["
removeAttributes
"]가 exists인 경우:-
attributes를 « »로 둡니다.
-
For each attribute of configuration["
removeAttributes
"] 에 대해 다음을 수행합니다:-
Append canonicalize a sanitizer attribute attribute의 결과를 attributes에 추가합니다.
-
-
configuration["
removeAttributes
"]를 attributes로 설정합니다.
-
-
configuration["
comments
"]가 exist하지 않으면, set configuration["comments
"]를 allowCommentsAndDataAttributes로 설정합니다. -
configuration["
attributes
"]가 exists이고 configuration["dataAttributes
"]가 exist하지 않으면, set configuration["dataAttributes
"]를 allowCommentsAndDataAttributes로 설정합니다.
SanitizerElementWithAttributes
element에 대해 다음을 수행합니다:
-
result를 canonicalize a sanitizer element를 element로 호출한 결과로 둡니다.
-
element가 dictionary인 경우:
-
element["
attributes
"]가 exists이면:-
attributes를 « »로 둡니다.
-
For each attribute of element["
attributes
"]:-
Append canonicalize a sanitizer attribute를 attribute로 호출한 결과를 attributes에 추가합니다.
-
-
Set result["
attributes
"]를 attributes로 설정합니다.
-
-
element["
removeAttributes
"]가 exists이면:-
attributes를 « »로 둡니다.
-
For each attribute of element["
removeAttributes
"]:-
Append canonicalize a sanitizer attribute를 attribute로 호출한 결과를 attributes에 추가합니다.
-
-
Set result["
removeAttributes
"]를 attributes로 설정합니다.
-
-
-
result["
attributes
"]와 result["removeAttributes
"]가 모두 exist하지 않으면:-
Set result["
removeAttributes
"]를 « »로 설정합니다.
-
-
result를 반환합니다.
SanitizerElement
element에 대해,
canonicalize a sanitizer name을
element와 HTML namespace를 기본 네임스페이스로 하여 호출한 결과를 반환합니다.
SanitizerAttribute
attribute에 대해,
canonicalize a sanitizer name을
attribute와 기본 네임스페이스로 null을 사용하여 호출한 결과를 반환합니다.
-
Assert: name은
DOMString
이거나 dictionary여야 합니다. -
name이
DOMString
이면, «[ "name
" → name, "namespace
" → defaultNamespace]»를 반환합니다. -
Assert: name은 dictionary이며 name["name"]과 name["namespace"]가 모두 exist합니다.
-
name["namespace"]가 빈 문자열이면, 이를 null로 설정합니다.
-
«[
"name
" → name["name"],
"namespace
" → name["namespace"]
]»를 반환합니다.
3.5. 보조 알고리즘
이 명세에서 사용하는 정규화된
요소
및 속성 이름
목록에서, 목록 멤버십은 "name
"과 "namespace
" 두 항목의 매칭을 기반으로 합니다:
-
만약 list가 이미 name를 포함하면 반환합니다.
-
Append로 name을 list에 추가합니다.
-
itemA["namespace"]가 null인 경우:
-
itemB["namespace"]가 null이 아니면, true를 반환한다.
-
-
그렇지 않으면:
-
itemB["namespace"]가 null이면, false를 반환한다.
-
itemA["namespace"]가 code unit less than itemB["namespace"]이면, true를 반환한다.
-
-
itemA["name"]이 code unit less than itemB["name"]인지 반환한다.
SanitizerElement
를 담고 있을 때, 집합의 교집합과 동일하되,
그 전에 집합의 항목들을 미리 정규화한 것으로 정의된다:
-
set A를 « [] »로 둔다.
-
set B를 « [] »로 둔다.
-
각 entry ∈ A에 대해, append를 사용해 canonicalize a sanitizer name의 결과로 얻은 entry를 set A에 추가한다.
-
각 entry ∈ B에 대해, append를 사용해 canonicalize a sanitizer name의 결과로 얻은 entry를 set B에 추가한다.
-
set A와 set B의 교집합을 반환한다.
3.6. 내장 항목
내장 항목은 네 가지가 있습니다:
내장 안전 기본 구성(built-in safe default configuration)는 다음과 같습니다:
{ "elements" : [ { "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" : "fence" , "namespace" : null }, { "name" : "form" , "namespace" : null }, { "name" : "largeop" , "namespace" : null }, { "name" : "lspace" , "namespace" : null }, { "name" : "maxsize" , "namespace" : null }, { "name" : "minsize" , "namespace" : null }, { "name" : "movablelimits" , "namespace" : null }, { "name" : "rspace" , "namespace" : null }, { "name" : "separator" , "namespace" : null }, { "name" : "stretchy" , "namespace" : null }, { "name" : "symmetric" , "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" : "depth" , "namespace" : null }, { "name" : "height" , "namespace" : null }, { "name" : "lspace" , "namespace" : null }, { "name" : "voffset" , "namespace" : null }, { "name" : "width" , "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" : "depth" , "namespace" : null }, { "name" : "height" , "namespace" : null }, { "name" : "width" , "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" : "a" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "href" , "namespace" : null }, { "name" : "hreflang" , "namespace" : null }, { "name" : "rel" , "namespace" : null }, { "name" : "type" , "namespace" : null } ] }, { "name" : "abbr" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "address" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "article" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "aside" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "b" , "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" : "blockquote" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "cite" , "namespace" : null } ] }, { "name" : "body" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "br" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "caption" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "cite" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "code" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "col" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "span" , "namespace" : null } ] }, { "name" : "colgroup" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "span" , "namespace" : null } ] }, { "name" : "data" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "value" , "namespace" : null } ] }, { "name" : "dd" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "del" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "cite" , "namespace" : null }, { "name" : "datetime" , "namespace" : null } ] }, { "name" : "dfn" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "div" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "dl" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "dt" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "em" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "figcaption" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "figure" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "footer" , "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" : "head" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "header" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "hgroup" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "hr" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "html" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "i" , "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" : "kbd" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "li" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "value" , "namespace" : null } ] }, { "name" : "main" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "mark" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "menu" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "nav" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "ol" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "reversed" , "namespace" : null }, { "name" : "start" , "namespace" : null }, { "name" : "type" , "namespace" : null } ] }, { "name" : "p" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "pre" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "q" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "rp" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "rt" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "ruby" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "s" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "samp" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "search" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "section" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "small" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "span" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "strong" , "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" : "table" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "tbody" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "td" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "colspan" , "namespace" : null }, { "name" : "headers" , "namespace" : null }, { "name" : "rowspan" , "namespace" : null } ] }, { "name" : "tfoot" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "th" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "abbr" , "namespace" : null }, { "name" : "colspan" , "namespace" : null }, { "name" : "headers" , "namespace" : null }, { "name" : "rowspan" , "namespace" : null }, { "name" : "scope" , "namespace" : null } ] }, { "name" : "thead" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "time" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "datetime" , "namespace" : null } ] }, { "name" : "title" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "tr" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "u" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "ul" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "var" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "wbr" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "circle" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "cx" , "namespace" : null }, { "name" : "cy" , "namespace" : null }, { "name" : "pathLength" , "namespace" : null }, { "name" : "r" , "namespace" : null } ] }, { "name" : "defs" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [] }, { "name" : "desc" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [] }, { "name" : "ellipse" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "cx" , "namespace" : null }, { "name" : "cy" , "namespace" : null }, { "name" : "pathLength" , "namespace" : null }, { "name" : "rx" , "namespace" : null }, { "name" : "ry" , "namespace" : null } ] }, { "name" : "foreignObject" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "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" : "line" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "pathLength" , "namespace" : null }, { "name" : "x1" , "namespace" : null }, { "name" : "x2" , "namespace" : null }, { "name" : "y1" , "namespace" : null }, { "name" : "y2" , "namespace" : null } ] }, { "name" : "marker" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "markerHeight" , "namespace" : null }, { "name" : "markerUnits" , "namespace" : null }, { "name" : "markerWidth" , "namespace" : null }, { "name" : "orient" , "namespace" : null }, { "name" : "preserveAspectRatio" , "namespace" : null }, { "name" : "refX" , "namespace" : null }, { "name" : "refY" , "namespace" : null }, { "name" : "viewBox" , "namespace" : null } ] }, { "name" : "metadata" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [] }, { "name" : "path" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "d" , "namespace" : null }, { "name" : "pathLength" , "namespace" : null } ] }, { "name" : "polygon" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "pathLength" , "namespace" : null }, { "name" : "points" , "namespace" : null } ] }, { "name" : "polyline" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "pathLength" , "namespace" : null }, { "name" : "points" , "namespace" : null } ] }, { "name" : "rect" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "height" , "namespace" : null }, { "name" : "pathLength" , "namespace" : null }, { "name" : "rx" , "namespace" : null }, { "name" : "ry" , "namespace" : null }, { "name" : "width" , "namespace" : null }, { "name" : "x" , "namespace" : null }, { "name" : "y" , "namespace" : null } ] }, { "name" : "svg" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "height" , "namespace" : null }, { "name" : "preserveAspectRatio" , "namespace" : null }, { "name" : "viewBox" , "namespace" : null }, { "name" : "width" , "namespace" : null }, { "name" : "x" , "namespace" : null }, { "name" : "y" , "namespace" : null } ] }, { "name" : "text" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "dx" , "namespace" : null }, { "name" : "dy" , "namespace" : null }, { "name" : "lengthAdjust" , "namespace" : null }, { "name" : "rotate" , "namespace" : null }, { "name" : "textLength" , "namespace" : null }, { "name" : "x" , "namespace" : null }, { "name" : "y" , "namespace" : null } ] }, { "name" : "textPath" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "lengthAdjust" , "namespace" : null }, { "name" : "method" , "namespace" : null }, { "name" : "path" , "namespace" : null }, { "name" : "side" , "namespace" : null }, { "name" : "spacing" , "namespace" : null }, { "name" : "startOffset" , "namespace" : null }, { "name" : "textLength" , "namespace" : null } ] }, { "name" : "title" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [] }, { "name" : "tspan" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "dx" , "namespace" : null }, { "name" : "dy" , "namespace" : null }, { "name" : "lengthAdjust" , "namespace" : null }, { "name" : "rotate" , "namespace" : null }, { "name" : "textLength" , "namespace" : null }, { "name" : "x" , "namespace" : null }, { "name" : "y" , "namespace" : null } ] } ], "attributes" : [ { "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" : "dir" , "namespace" : null }, { "name" : "direction" , "namespace" : null }, { "name" : "display" , "namespace" : null }, { "name" : "displaystyle" , "namespace" : null }, { "name" : "dominant-baseline" , "namespace" : null }, { "name" : "fill" , "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" : "lang" , "namespace" : null }, { "name" : "letter-spacing" , "namespace" : null }, { "name" : "marker-end" , "namespace" : null }, { "name" : "marker-mid" , "namespace" : null }, { "name" : "marker-start" , "namespace" : null }, { "name" : "mathbackground" , "namespace" : null }, { "name" : "mathcolor" , "namespace" : null }, { "name" : "mathsize" , "namespace" : null }, { "name" : "opacity" , "namespace" : null }, { "name" : "paint-order" , "namespace" : null }, { "name" : "pointer-events" , "namespace" : null }, { "name" : "scriptlevel" , "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" : "title" , "namespace" : null }, { "name" : "transform" , "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" : "embed" }, { "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" : "script" }, { "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" : "embed" }, { "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" : "script" }, { "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:
"
네비게이션이 "비안전"으로 간주되는 경우로, 다음과 같습니다:
«[
[
{ "name
" → "a
", "namespace
" → HTML
네임스페이스
},
{ "name
" → "href
", "namespace
" → null
}
],
[
{ "name
" → "area
", "namespace
" → HTML
네임스페이스
},
{ "name
" → "href
", "namespace
" → null
}
],
[
{ "name
" → "base
", "namespace
" → HTML
네임스페이스
},
{ "name
" → "href
", "namespace
" → null
}
],
[
{ "name
" → "button
", "namespace
" → HTML
네임스페이스
},
{ "name
" → "formaction
", "namespace
" → null
}
],
[
{ "name
" → "form
", "namespace
" → HTML
네임스페이스
},
{ "name
" → "action
", "namespace
" → null
}
],
[
{ "name
" → "iframe
", "namespace
" → HTML
네임스페이스
},
{ "name
" → "src
", "namespace
" → null
}
],
[
{ "name
" → "input
", "namespace
" → HTML
네임스페이스
},
{ "name
" → "formaction
", "namespace
" → null
}
],
[
{ "name
" → "a
", "namespace
" → SVG 네임스페이스
},
{ "name
" → "href
", "namespace
" → null
}
],
[
{ "name
" → "a
", "namespace
" → SVG 네임스페이스
},
{ "name
" → "href
", "namespace
" → XLink
네임스페이스
}
],
]»
내장
애니메이션 URL 속성 리스트는
[SVG11]에서
선언적으로 네비게이션 요소를 "javascript:
" URL로 변경하는 데 사용할 수 있으며, 다음과 같습니다:
«[
[
{ "name
" → "animate
", "namespace
" → SVG 네임스페이스 },
{ "name
" → "attributeName
", "namespace
" → null
] }
],
[
{ "name
" → "animateMotion
", "namespace
" → SVG 네임스페이스 },
{ "name
" → "attributeName
", "namespace
" → null
}
],
[
{ "name
" → "animateTransform
", "namespace
" → SVG 네임스페이스 },
{ "name
" → "attributeName
", "namespace
" → null
}
],
[
{ "name
" → "set
", "namespace
" → SVG 네임스페이스 },
{ "name
" → "attributeName
", "namespace
" → null
}
],
]»
4. 보안 고려사항
Sanitizer API는 DOM 기반 크로스 사이트 스크립팅(XSS)을 방지하기 위해 고안되었습니다. 제공받은 HTML 콘텐츠를 순회하며, 구성(configuration)에 따라 요소와 속성을 제거합니다. 명세된 API는 스크립트 실행이 가능한 마크업이 남아있는 Sanitizer 객체를 생성하는 것을 지원해서는 안 되며, 만약 그렇게 된다면 이는 위협 모델의 버그입니다.
그럼에도 불구하고 Sanitizer API의 올바른 사용으로도 보호할 수 없는 보안 이슈들이 있으며, 그 시나리오들은 아래의 섹션에서 설명됩니다.
4.1. 서버 측 반사 및 저장 XSS
이 섹션은 규범적이지 않습니다.
Sanitizer API는 DOM 내에서만 동작하며, 기존 DocumentFragment를 순회하고 필터링하는 기능을 제공합니다. Sanitizer는 서버 측 반사(reflected) 또는 저장(stored) XSS를 다루지 않습니다.
4.2. DOM 클로버링 공격
이 섹션은 규범적이지 않습니다.
DOM 클로버링(clobbering)은 악의적인 HTML이 id
또는 name
속성으로 요소의 이름을 지정하여,
DOM 내 HTML 요소의 children
과 같은 프로퍼티가 악성 콘텐츠로 인해 덮어써지는 공격입니다.
Sanitizer API는 기본 상태에서는 DOM 클로버링 공격을 방지하지 않지만,
id
와 name
속성을 제거하도록 구성할 수 있습니다.
4.3. 스크립트 가젯을 이용한 XSS
이 섹션은 규범적이지 않습니다.
스크립트 가젯(script gadgets)은 공격자가 인기 있는 JavaScript 라이브러리의 기존 애플리케이션 코드를 악용하여, 자신의 코드를 실행시키는 기술입니다. 이는 보기에는 무해한 코드 또는 동작하지 않을 것 같은 DOM 노드를 삽입하고, 프레임워크가 그 입력을 해석하여 JavaScript 실행을 유도하는 방식으로 이루어집니다.
Sanitizer API는 이러한 공격을 방지할 수 없지만, 페이지 작성자가 일반적으로 알려지지 않은 요소를 명시적으로 허용해야 하며,
또한 알려지지 않은 속성과 요소, 그리고 템플릿이나 프레임워크 전용 코드에 자주 사용되는 마크업(예: data-
, slot
속성 및
<slot>
, <template>
요소 등)을 명시적으로 구성해야 합니다.
이러한 제한이 충분하지 않다고 생각하며, 페이지 작성자는 해당 동작을 위해 사용하는 서드파티 라이브러리를 반드시 검토해야 합니다.
4.4. Mutated XSS
이 섹션은 규범적이지 않습니다.
Mutated XSS 또는 mXSS는 HTML 스니펫을 올바르지 않은 문맥(context)에서 파싱할 때 발생하는 파서 컨텍스트 불일치에 기반한 공격입니다. 특히 파싱된 HTML 프래그먼트를 문자열로 직렬화한 후, 그 문자열을 다른 부모 요소에 삽입하여 다시 파싱할 때 원래와 정확히 동일하게 해석된다는 보장이 없습니다. 예를 들어, 외래 콘텐츠(foreign content) 또는 잘못 중첩된 태그(mis-nested tags)에 대한 파싱 방식이 바뀌는 점을 악용할 수 있습니다.
Sanitizer API는 문자열을 노드 트리로 변환하는 기능만 제공합니다.
모든 sanitizer 함수는 암묵적으로 컨텍스트를 제공합니다:
Element.setHTML()
은 현재 요소를 사용하고, Document.parseHTML()
은 새 문서를 만듭니다.
따라서 Sanitizer API는 mutated XSS에 직접적으로 영향을 받지 않습니다.
만약 개발자가 Sanitizer로 정화된 노드 트리를 .innerHTML
등으로 문자열로 얻은 후,
다시 파싱한다면 mutated XSS가 발생할 수 있습니다.
우리는 이러한 사용을 권장하지 않습니다. HTML을 문자열로 처리하거나 전달해야만 하는 경우,
해당 문자열은 신뢰할 수 없는 것으로 간주하고 DOM에 삽입할 때 반드시 다시 한 번 Sanitizer로 정화해야 합니다.
즉, Sanitizer로 정화한 후 직렬화된 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에게 소중한 피드백을 주셔서 감사합니다.