1. 소개
이 절은 비규범적입니다.
웹 애플리케이션은 클라이언트 측에서 HTML 문자열을 다뤄야 하는 경우가 자주 있습니다.
클라이언트 측 템플릿 솔루션의 일부일 수도 있고, 사용자 생성 콘텐츠를 렌더링하는 과정의 일부일 수도 있습니다.
이를 안전한 방식으로 수행하는 것은 어렵습니다.
문자열을 단순히 이어 붙여
Element의
innerHTML
에 밀어 넣는 순진한 접근은 수많은 예상치 못한 방식으로
JavaScript 실행을 유발할 수 있어 매우 위험합니다.
[DOMPURIFY]와 같은 라이브러리는 삽입 전에 문자열을 신중히 파싱하고 정리(sanitize)하여 문제를 다루려 합니다. 즉, DOM을 구성한 다음 허용 목록을 통해 그 구성원을 필터링합니다. 하지만 이는 취약한 접근법으로 드러났습니다. 웹에 노출된 파싱 API는 실제로 문자열을 "실제" DOM으로 렌더링할 때의 브라우저 동작과 항상 합리적으로 매핑되지 않기 때문입니다. 게다가 라이브러리는 시간이 지나며 변화하는 브라우저 동작을 계속 따라가야 합니다. 한때 안전했던 것이 플랫폼 수준의 새로운 기능으로 인해 시한폭탄이 될 수 있습니다.
브라우저는 언제 코드를 실행할지에 대해 꽤 잘 알고 있습니다. 사용자 공간 라이브러리보다 더 나은 방법은, 브라우저가 임의 문자열로부터 HTML을 안전한 방식으로 렌더링하도록 가르치고, 브라우저 자체의 파서 구현이 변함에 따라 함께 유지·업데이트될 가능성이 훨씬 높은 방식으로 이를 수행하는 것입니다. 이 문서는 바로 그러한 목적을 달성하려는 API를 개략적으로 설명합니다.
1.1. 목표
-
개발자에게 사용자 제어 HTML을 처리하는 메커니즘을 제공하여, 주입 시 스크립트가 직접 실행되는 것을 방지함으로써 DOM 기반 크로스 사이트 스크립팅(XSS) 공격의 위험을 완화합니다.
-
현재 사용자 에이전트의 HTML에 대한 이해를 고려하여, 해당 사용자 에이전트 내에서 안전하게 사용할 수 있는 HTML 출력을 만듭니다.
-
개발자가 기본 요소 및 속성 집합을 재정의할 수 있도록 합니다. 특정 요소와 속성을 추가하면 스크립트 가젯 공격을 방지할 수 있습니다.
1.2. API 요약
정리자 API는 HTML을 포함하는 문자열을 DOM 트리로 파싱하고, 사용자가 제공한 구성에 따라 결과 트리를 필터링하는 기능을 제공합니다. 메서드는 두 가지 맛으로 제공됩니다:
-
안전 및 비안전: "안전" 메서드는 스크립트를 실행하는 마크업을 생성하지 않습니다. 즉, XSS로부터 안전해야 합니다. "비안전" 메서드는 요구된 대로 파싱하고 필터링합니다. 또한 § 4 보안 고려사항을 참고하세요.
-
컨텍스트: 메서드는
Element및ShadowRoot에 정의되며 이들Node의 자식을 교체합니다. 이는 대체로innerHTML과 유사합니다. 또한Document에 정적 메서드가 있으며, 이는 전체 문서를 파싱하고 대체로DOMParser.parseFromString()과 유사합니다.
2. 프레임워크
2.1. 정리자 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을 신뢰된 타입 호환 문자열 얻기 알고리즘에
TrustedHTML, this의 관련 전역 객체, html, "Element setHTMLUnsafe", 그리고 "script"를 전달하여 호출한 결과로 한다. -
target을 다음으로 둡니다: this가
template요소인 경우 template contents, 그 외에는 this. -
Set and filter HTML을 target, this, compliantHTML, options, false를 인수로 하여 수행합니다.
Element의
setHTML(html, options) 메서드 단계는
다음과 같습니다:
-
target을 다음으로 둡니다: this가
template인 경우 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을 신뢰된 타입 호환 문자열 가져오기 알고리즘에
TrustedHTML, this의 관련 전역 객체, 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
parseHTMLUnsafe(html, options)
메서드 단계는 다음과 같습니다:
-
compliantHTML을 get trusted type compliant string 알고리즘에
TrustedHTML, 현재 전역 객체, 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를 반환합니다.
parseHTML(html, options)
메서드 단계는 다음과 같습니다:
-
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"); // 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
구성이 있습니다.
constructor(configuration)
메서드 단계는 다음과 같습니다:
-
configuration이
SanitizerPresets문자열인 경우:-
configuration을 내장 안전 기본 구성으로 설정합니다.
-
valid가 false이면
TypeError를 던집니다.
get() 메서드 단계는 다음과 같다:
-
config를 this의 configuration으로 둔다.
-
-
모든 config["
elements"]의 element에 대하여:-
만약 element["
attributes"]가 존재한다면:-
attrA가 항목보다 작음 attrB이 되도록, element["
attributes"]를 오름차순으로 정렬한 결과로 element["attributes"]를 설정한다.
-
-
만약 element["
removeAttributes"]가 존재한다면:-
attrA가 항목보다 작음 attrB이 되도록, element["
removeAttributes"]를 오름차순으로 정렬한 결과로 element["removeAttributes"]를 설정한다.
-
-
-
elementA가 항목보다 작음 elementB가 되도록, config["
elements"]를 오름차순으로 정렬한 결과로 설정한다.
-
-
그렇지 않으면:
-
elementA가 항목보다 작음 elementB가 되도록, config["
removeElements"]를 오름차순으로 정렬한 결과로 설정한다.
-
-
만약 config["
replaceWithChildrenElements"]가 존재한다면:-
elementA가 항목보다 작음 elementB가 되도록, config["
replaceWithChildrenElements"]를 오름차순으로 정렬한 결과로 설정한다.
-
-
만약 config["
attributes"]가 존재한다면:-
attrA가 항목보다 작음 attrB가 되도록, config["
attributes"]를 오름차순으로 정렬한 결과로 설정한다.
-
-
그렇지 않으면:
-
attrA가 항목보다 작음 attrB가 되도록, config["
removeAttributes"]를 오름차순으로 정렬한 결과로 설정한다.
-
-
config를 반환한다.
allowElement(element) 메서드 단계는 다음과 같다:
-
전역 허용 목록인지 전역 제거 목록인지, 그리고
-
이 목록들에 이미 element가 포함되어 있는지 여부.
-
configuration을 this의 configuration으로 둔다.
-
element를 속성이 있는 Sanitizer 요소를 정규화한 결과로 설정한다.
-
만약 configuration["
elements"]가 존재한다면:-
modified를 제거의 결과로 설정하는데, configuration["
replaceWithChildrenElements"]에서 element를 제거한 결과로 한다. -
주석: 요소별 속성이 전역 속성과 겹치지 않도록 해야 한다.
-
만약 configuration["
attributes"]가 존재한다면:-
만약 element["
attributes"]가 존재한다면:-
element["
attributes"]를 중복 제거한 결과로 설정한다. -
element["
attributes"]를 차집합의 결과로 설정하는데, element["attributes"]에서 configuration["attributes"]를 뺀 값으로 한다. -
만약 configuration["
dataAttributes"]가 true라면:-
모든 항목 item을 element["
attributes"]에서 제거하되, item이 사용자 정의 데이터 속성인 경우에 한한다.
-
-
-
만약 element["
removeAttributes"]가 존재한다면:-
element["
removeAttributes"]를 중복 제거한 결과로 설정한다. -
element["
removeAttributes"]를 교집합의 결과로 설정하는데, element["removeAttributes"]와 configuration["attributes"]의 교집합으로 한다.
-
-
-
그렇지 않으면:
-
만약 element["
attributes"]가 존재한다면:-
element["
attributes"]를 중복 제거한 결과로 설정한다. -
element["
attributes"]를 차집합의 결과로 설정하는데, element["attributes"]에서 element["removeAttributes"]를 기본값과 함께 « »로 간주해 뺀 값으로 한다. -
제거: element["
removeAttributes"]를 제거한다. -
element["
attributes"]를 차집합의 결과로 설정하는데, element["attributes"]에서 configuration["removeAttributes"]를 뺀 값으로 한다.
-
-
만약 element["
removeAttributes"]가 존재한다면:-
element["
removeAttributes"]를 중복 제거한 결과로 설정한다. -
element["
removeAttributes"]를 차집합의 결과로 설정하는데, element["removeAttributes"]에서 configuration["removeAttributes"]를 뺀 값으로 한다.
-
-
-
주석: 전역 허용 목록에 이미 element가 있는 경우이다.
-
current element를 configuration["
elements"]의 item 가운데에서, item[name]이 같고 element[name]과, item[namespace]가 같은 element[namespace]인 항목으로 둔다. -
만약 element가 동일하다면 current element와, modified를 반환한다.
-
true를 반환한다.
-
-
그렇지 않으면:
-
만약 element["
attributes"]가 존재하거나 element["removeAttributes"]를 기본값과 함께 « »로 보았을 때 비어 있지 않다면:-
사용자 에이전트는 이 동작이 지원되지 않음을 콘솔에 경고로 보고할 수 있다.
-
false를 반환한다.
-
-
modified를 제거의 결과로 설정하는데, configuration["
replaceWithChildrenElements"]에서 element를 제거한 결과로 한다. -
만약 configuration["
removeElements"]가 포함하지 않는다면 element를:-
주석: 전역 제거 목록에 element가 없는 경우이다.
-
modified를 반환한다.
-
-
주석: 전역 제거 목록에 element가 있는 경우이다.
-
제거: configuration["
removeElements"]에서 element를 제거한다. -
true를 반환한다.
-
replaceElementWithChildren(element)
메서드 단계는 다음과 같다:
-
configuration을 this의 configuration으로 둔다.
-
element를 Sanitizer 요소를 정규화한 결과로 설정한다.
-
만약 configuration["
replaceWithChildrenElements"]가 포함한다면 element를:-
false를 반환한다.
-
-
제거: configuration["
removeElements"]에서 element를 제거한다. -
추가: configuration["
replaceWithChildrenElements"]에 element를 추가한다. -
true를 반환한다.
allowAttribute(attribute) 메서드 단계는
다음과 같다:
-
configuration을 this의 configuration으로 둔다.
-
attribute를 Sanitizer 속성을 정규화한 결과로 설정한다.
-
만약 configuration["
attributes"]가 존재한다면:-
주석: 전역 허용 목록이 있다면 attribute를 추가해야 한다.
-
만약 configuration["
dataAttributes"]가 true이고, attribute가 사용자 정의 데이터 속성이라면, false를 반환한다. -
만약 configuration["
attributes"]가 포함한다면 attribute를 false를 반환한다. -
주석: 요소별 허용 및 제거 목록을 정리한다.
-
만약 configuration["
elements"]가 존재한다면:-
각 element에 대해, configuration["
elements"]에 있는:-
만약 element["
attributes"]를 기본값과 함께 « »로 보았을 때 포함한다면 attribute를:-
제거: element["
attributes"]에서 attribute를 제거한다.
-
-
단언: element["
removeAttributes"]를 기본값과 함께 « »로 보았을 때 포함하지 않아야 한다 attribute.
-
-
-
추가: configuration["
attributes"]에 attribute를 추가한다. -
true를 반환한다.
-
-
그렇지 않으면:
-
주석: 전역 제거 목록이 있는 경우, attribute를 제거해야 한다.
-
만약 configuration["
removeAttributes"]가 포함하지 않는다면 attribute를:-
false를 반환한다.
-
-
제거: configuration["
removeAttributes"]에서 attribute를 제거한다. -
true를 반환한다.
-
setComments(allow) 메서드 단계는 다음과 같다:
setDataAttributes(allow) 메서드 단계는
다음과 같다:
-
configuration을 this의 configuration으로 둔다.
-
만약 configuration["
attributes"]가 존재하지 않는다면, false를 반환한다. -
만약 configuration["
dataAttributes"]가 allow와 같다면, false를 반환한다. -
만약 allow가 true라면:
-
제거: 모든 항목 attr을 configuration["
attributes"]에서 제거하되, attr이 사용자 정의 데이터 속성인 경우에 한한다. -
만약 configuration["
elements"]가 존재한다면:-
각 element에 대해, configuration["
elements"]에 있는:-
만약 element[
attributes]가 존재한다면:-
제거: 모든 항목 attr을 element[
attributes]에서 제거하되, attr이 사용자 정의 데이터 속성인 경우에 한한다.
-
-
-
-
-
configuration["
dataAttributes"]를 allow로 설정한다. -
true를 반환한다.
removeUnsafe() 메서드 단계는
this의 configuration을
안전하지 않은 것을 제거를
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를 « »로 set한 것과 동등하다. -
attributes또는removeAttributes는 존재할 수 있으나, 둘 다 동시에 존재할 수는 없다. 둘 다 없으면 이는removeAttributes를 « »로 set한 것과 동등하다. -
dataAttributes는 개념적으로attributes허용 목록의 확장이다.dataAttributes속성은attributes목록이 사용될 때에만 허용된다.
-
-
서로 다른 전역 목록 간의 중복 항목:
-
elements,removeElements, 또는replaceWithChildrenElements사이에는 중복 항목(즉, 동일한 요소)이 없어야 한다. -
attributes와removeAttributes사이에는 중복 항목(즉, 동일한 속성)이 없어야 한다.
-
-
같은 요소에서 로컬 허용/제거 목록을 혼합하는 경우:
-
attributes목록이 존재하는 경우, 동일한 요소에서attributes와removeAttributes목록을 둘 다 사용하거나, 둘 중 하나만 사용하거나, 둘 다 사용하지 않을 수 있다. -
removeAttributes목록이 존재하는 경우, 동일한 요소에서attributes와removeAttributes목록 중 하나만 사용하거나 둘 다 사용하지 않을 수 있으나, 둘 다 동시에 사용할 수는 없다.
-
-
같은 요소에서의 중복 항목:
-
같은 요소에서
attributes와removeAttributes사이에는 중복 항목이 없어야 한다.
-
elements
요소 허용 목록은 특정 요소에 대해 속성을 허용하거나 제거하도록 지정할 수도 있다. 이는 [HTML]의 구조를 반영하기 위한 것으로,
전역 속성과 특정 요소에 적용되는 로컬 속성을
모두 인지한다. 전역 및 로컬 속성은 혼용할 수 있으나, 어떤 속성이 한 목록에서는 허용되고 다른 목록에서는 금지되는 등
모호한 구성은 일반적으로 유효하지 않음을 유의하라.
전역 attributes
| 전역 removeAttributes
| |
|---|---|---|
로컬 attributes
| 속성은 둘 중 어느 목록과 일치하면 허용된다. 중복은 허용되지 않는다. | 속성은 로컬 허용 목록에 있는 경우에만 허용된다. 전역 제거 목록과 로컬 허용 목록 사이에는 중복 항목이 허용되지 않는다. 전역 제거 목록은 이 특정 요소에 대해서는 기능하지 않지만, 로컬 허용 목록이 없는 다른 요소에는 적용될 수 있음을 유의하라. |
로컬 removeAttributes
| 속성은 전역 허용 목록에 있고 로컬 제거 목록에는 없는 경우 허용된다. 로컬 제거 목록은 전역 허용 목록의 부분집합이어야 한다. | 속성은 어느 목록에도 없을 때 허용된다. 전역 제거 목록과 로컬 제거 목록 사이에는 중복 항목이 허용되지 않는다. |
전역 목록과 요소별 목록 사이에서는 대체로 중복이 허용되지 않지만, 전역 허용 목록과 요소별 제거 목록의 조합에서는 후자가 전자에 대한 부분집합이어야 한다는 비대칭성이 있음을 유의하라. 위 표에서 중복에만 초점을 맞춘 발췌는 다음과 같다:
전역 attributes
| 전역 removeAttributes
| |
|---|---|---|
로컬 attributes
| 중복은 허용되지 않는다. | 중복은 허용되지 않는다. |
로컬 removeAttributes
| 로컬 제거 목록은 전역 허용 목록의 부분집합이어야 한다. | 중복은 허용되지 않는다. |
dataAttributes
설정은 사용자 정의 데이터 속성을 허용한다. 위 규칙은
dataAttributes
를 허용 목록으로 간주하면 사용자 정의 데이터 속성에도 쉽게 확장된다:
전역 attributes
와 dataAttributes
가 설정됨
| |
|---|---|
로컬 attributes
| 모든 사용자 정의 데이터 속성이 허용된다. 사용자 정의 데이터 속성은 중복 항목이 되므로 어떤 허용 목록에도 기재되어서는 안 된다. |
로컬 removeAttributes
| 사용자 정의 데이터 속성은 로컬 제거 목록에 기재되지 않은 한 허용된다. 중복 항목이 되므로 전역 허용 목록에는 사용자 정의 데이터 속성을 기재해서는 안 된다. |
이 규칙들을 문장으로 정리하면:
-
전역 목록과 로컬 목록 사이의 중복과 상호작용:
-
전역
attributes허용 목록이 존재하면, 모든 요소의 로컬 목록에 대해:-
로컬
attributes허용 목록이 존재하는 경우, 이들 목록 사이에는 중복 항목이 없어야 한다. -
로컬
removeAttributes제거 목록이 존재하는 경우, 그 모든 항목은 전역attributes허용 목록에도 기재되어 있어야 한다. -
dataAttributes가 true인 경우, 어떤 허용 목록에도 사용자 정의 데이터 속성을 기재해서는 안 된다.
-
-
전역
removeAttributes제거 목록이 존재하면:-
로컬
attributes허용 목록이 존재하는 경우, 이들 목록 사이에는 중복 항목이 없어야 한다. -
로컬
removeAttributes제거 목록이 존재하는 경우, 이들 목록 사이에는 중복 항목이 없어야 한다. -
로컬
attributes허용 목록과 로컬removeAttributes제거 목록이 동시에 존재해서는 안 된다. -
dataAttributes는 없어야 한다.
-
-
SanitizerConfig
config가 다음 모든 조건을 만족하면
유효하다:
-
config에는
elements또는removeElements키가 있어야 하며, 둘 다 동시에 있어서는 안 된다. -
config에는
attributes또는removeAttributes키가 있어야 하며, 둘 다 동시에 있어서는 안 된다. -
단언: config 내의 모든
SanitizerElementNamespaceWithAttributes,SanitizerElementNamespace, 및SanitizerAttributeNamespace항목들은 정규형이어야 하며, 이는 상황에 맞게 Sanitizer 요소 정규화 또는 Sanitizer 속성 정규화를 거쳤음을 의미한다. -
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] 가 존재한다면:-
-
각 config[
elements]의 element에 대해:-
element[
attributes] 와 element[removeAttributes] 는(존재한다면) 중복 항목이 없어야 한다. -
config[
attributes] 와 element[attributes] 기본값 « » 을 사용한 교집합은 비어 있어야 한다. -
element[
removeAttributes] 기본값 « » 은 config[attributes] 의 부분집합이어야 한다. -
dataAttributes가 존재하고dataAttributes가 true라면:-
element[
attributes] 에는 사용자 정의 데이터 속성이 포함되지 않아야 한다.
-
-
-
-
Assert: config[
dataAttributes] 존재함. -
dataAttributes가 true라면:-
config[
attributes] 에는 사용자 정의 데이터 속성이 포함되지 않아야 한다.
-
-
-
config[
removeAttributes] 가 존재한다면:-
config[
elements] 가 존재한다면, 각 config[elements] 의 element에 대해:-
element[
attributes] 와 element[removeAttributes] 가 동시에 존재해서는 안 된다. -
element[
attributes] 와 element[removeAttributes] 는(존재한다면) 중복 항목이 없어야 한다. -
config[
removeAttributes] 와 element[attributes] 기본값 « » 을 사용한 교집합은 비어 있어야 한다. -
config[
removeAttributes] 와 element[removeAttributes] 기본값 « » 을 사용한 교집합은 비어 있어야 한다.
-
-
config[
dataAttributes] 는 존재하지 않아야 한다.
-
Note: 구성 설정을
dictionary에서 수행하면 약간의 정규화가 이루어진다.
특히 허용/제거 목록이 모두 없으면 이를 빈 제거 목록로 해석한다.
따라서 {} 자체는 유효한 구성은 아니지만,
{removeElements:[],removeAttributes:[]}로 정규화되며 이는 유효하다.
이 정규화 단계는 누락된 dictionary가 빈 dictionary와 일관되도록(즉,
setHTMLUnsafe(txt) 가 setHTMLUnsafe(txt, {sanitizer: {}}) 와
일치하도록) 선택된 것이다.
3. 알고리즘
Element
또는 DocumentFragment
target, Element
contextElement, 문자열 html, 딕셔너리
options, 불리언 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로 한다. -
newChildren의 각 node에 대해, fragment에 node 추가를 수행한다.
-
sanitize를 fragment, sanitizer, safe로 호출한다.
-
Replace all을 사용해 target 내에 fragment로 교체한다.
참고: 이 알고리즘은 SetHTMLOptions
와
SetHTMLUnsafeOptions
모두에 적용된다.
기본값만 다르다.
-
sanitizerSpec를 "
default"로 둔다. -
만약 options["
sanitizer"]가 존재한다면:-
sanitizerSpec를 options["
sanitizer"]로 설정한다.
-
-
단언: sanitizerSpec는
Sanitizer인스턴스이거나, string으로서SanitizerPresets멤버이거나, dictionary여야 한다. -
만약 sanitizerSpec가 string이라면:
-
sanitizerSpec를 내장 안전 기본 구성으로 설정한다.
-
단언: sanitizerSpec는
Sanitizer인스턴스이거나, dictionary여야 한다. -
만약 sanitizerSpec가 dictionary라면:
-
sanitizer를 새로운
Sanitizer인스턴스로 둔다. -
setConfigurationResult를 set a configuration을 sanitizer에서 sanitizerSpec 및 not safe와 함께 호출한 결과로 둔다.
-
만약 setConfigurationResult가 false라면, throw를 사용해
TypeError를 발생시킨다. -
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를 사용하여
node를 시작점으로 DOM 트리를 재귀적으로 순회한다. 다음 단계로 구성된다:
-
각 child에 대하여, node의 children 중에서:
-
단언: child는 implements를 만족하는
Text,Comment,Element, 또는DocumentType이다.참고: 현재 이 알고리즘은 HTML 파서의 출력에 대해서만 호출되며, 위 단언이 성립해야 한다.
DocumentType은parseHTML및parseHTMLUnsafe에서만 나타나야 한다. 향후 이 알고리즘이 다른 문맥에서 사용된다면, 이 가정은 재검토가 필요하다. -
만약 child가 implements
DocumentType라면, continue 한다. -
만약 child가 implements
Text라면, continue 한다. -
만약 child가 implements
Comment라면: -
그 밖의 경우:
-
elementName을
SanitizerElementNamespace로 두되, child의 local name과 namespace를 사용한다. -
만약 configuration["
replaceWithChildrenElements"] 가 존재하고, configuration["replaceWithChildrenElements"] 가 포함한다면 elementName을:-
sanitize core를 child에 대해 configuration 및 handleJavascriptNavigationUrls와 함께 호출한다.
-
replace all을 호출하여 child의 children을 child 내부에 대체한다.
-
-
그렇지 않다면:
-
만약 configuration["
removeElements"] 가 포함한다면 elementName을:
-
-
만약 elementName이 같다면 «[ "
name" → "template", "namespace" → HTML namespace ]», sanitize core를 child의 template contents에 대해 configuration 및 handleJavascriptNavigationUrls와 함께 호출한다. -
만약 child가 shadow host라면, sanitize core를 child의 shadow root에 대해 configuration 및 handleJavascriptNavigationUrls와 함께 호출한다.
-
elementWithLocalAttributes를 « [] »로 둔다.
-
만약 configuration["
elements"] 가 존재하고, configuration["elements"] 가 포함한다면 elementName을:-
elementWithLocalAttributes를 configuration["
elements"][elementName]로 설정한다.
-
-
각 attribute에 대해, child의 attribute list에서:
-
attrName을
SanitizerAttributeNamespace로 두되, attribute의 local name과 namespace를 사용한다. -
만약 elementWithLocalAttributes["
removeAttributes"] 가 with default « » 로 간주될 때 포함한다면 attrName을:-
Remove로 attribute를 제거한다.
-
-
그렇지 않고, configuration["
attributes"] 가 존재한다면:-
만약 configuration["
attributes"] 가 포함하지 않는다면 attrName을, 그리고 elementWithLocalAttributes["attributes"] 가 with default « » 로 간주될 때에도 포함하지 않는다면 attrName을, 그리고 "data-"가 code unit prefix가 아니고 attribute의 local name 에 대해, namespace가null이 아니거나 configuration["dataAttributes"] 가 true가 아니라면:-
Remove로 attribute를 제거한다.
-
-
-
그 밖의 경우:
-
만약 elementWithLocalAttributes["
attributes"] 가 존재하고, elementWithLocalAttributes["attributes"] 가 포함하지 않는다면 attrName을:-
Remove로 attribute를 제거한다.
-
-
그렇지 않고, configuration["
removeAttributes"] 가 포함한다면 attrName을:-
Remove로 attribute를 제거한다.
-
-
-
만약 handleJavascriptNavigationUrls라면:
-
만약 «[elementName, attrName]»가 내장 탐색 URL 속성 목록의 항목과 일치하고, attribute가 javascript: URL을 포함한다면, remove로 attribute를 제거한다.
-
만약 child의 namespace가 같고 MathML Namespace이며, attr의 local name이 다음과 같고 "
href"이며, attr의 namespace가null이거나 XLink namespace이고, attr가 javascript: URL을 포함한다면, remove로 attribute를 제거한다. -
만약 내장 애니메이팅 URL 속성 목록이 포함하고 «[elementName, attrName]»을, 그리고 attr의 value가 다음과 같다면 "
href" 또는 "xlink:href", remove로 attribute를 제거한다.
-
-
-
sanitize core를 child에 대해 configuration 및 handleJavascriptNavigationUrls와 함께 호출한다.
-
-
javascript: URL을 navigation 시에만 지원한다.
navigation 자체는 XSS 위협이 아니므로, navigation에서 javascript: URL만 막고 일반 navigation은 막지 않는다.
선언적 navigation은 다음과 같은 몇 가지 범주로 나뉜다:
-
앵커 요소 (
<a>, HTML 및 SVG 네임스페이스) -
form action에 따라 네비게이션을 트리거하는 폼 요소
-
[SVG11] 애니메이션.
첫 두 가지는 내장 navigating URL 속성 목록에서 다룬다.
MathML의 경우, 이 명세에 "네임스페이스 전역" 규칙이 없으므로 별도의 규칙으로 다룬다.
SVG 애니메이션의 경우는 내장 animating 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>`는 제거됨.
SanitizerElement
element를
SanitizerConfig
configuration에서 다음과 같이 처리한다:
-
전역 허용 목록인지 전역 제거 목록인지,
-
해당 목록에 이미 element가 포함되어 있는지 여부.
-
element를 sanitizer 요소를 정규화를 element로 수행한 결과로 설정한다.
-
modified를 remove를 호출해 configuration["
replaceWithChildrenElements"]에서 element를 제거한 결과로 설정한다. -
그렇지 않다면:
-
만약 configuration["
removeElements"]가 포함한다면 element를:-
주석: 전역 제거 목록이 있으며 이미 element가 포함되어 있다.
-
modified를 반환한다.
-
-
주석: 전역 제거 목록이 있으나 element는 포함되어 있지 않다.
-
Add를 호출해 element를 configuration["
removeElements"]에 추가한다. -
true를 반환한다.
-
SanitizerConfig
configuration에서 다음과 같이 처리한다:
참고: 이 메서드는 두 가지 경우를 구분한다. 전역 허용 목록이 있는지, 전역 제거 목록이 있는지이다. 전역 제거 목록에 attribute를 추가하는 경우, 유효성 기준을 유지하기 위해 요소별 허용/제거 목록을 정리하는 추가 작업이 필요할 수 있다. 전역 허용 목록에서 attribute를 제거하는 경우, 로컬 제거 목록에서도 이를 제거해야 할 수 있다.
-
attribute를 sanitizer 속성을 정규화를 attribute로 수행한 결과로 설정한다.
-
만약 configuration["
attributes"]가 존재한다면:-
주석: 전역 허용 목록이 있는 경우, attribute를 추가해야 한다.
-
만약 configuration["
attributes"]가 포함하지 않는다면 attribute를:-
false를 반환한다.
-
-
주석: 요소별 허용 및 제거 목록을 정리한다.
-
만약 configuration["
elements"]가 존재한다면:-
각 element에 대해 configuration["
elements"]에서:-
만약 element["
removeAttributes"]가 기본값과 함께 « »로 간주될 때 포함한다면 attribute를:-
Remove를 호출해 element["
removeAttributes"]에서 attribute를 제거한다.
-
-
-
-
Remove를 호출해 configuration["
attributes"]에서 attribute를 제거한다. -
true를 반환한다.
-
-
그렇지 않다면:
-
주석: 전역 제거 목록이 있는 경우, attribute를 추가해야 한다.
-
만약 configuration["
removeAttributes"]가 포함한다면 attribute를 false를 반환한다. -
주석: 요소별 허용 및 제거 목록을 정리한다.
-
만약 configuration["
elements"]가 존재한다면:-
각 element에 대해 configuration["
elements"]에서:-
만약 element["
attributes"]가 기본값과 함께 « »로 간주될 때 포함한다면 attribute를:-
Remove를 호출해 element["
attributes"]에서 attribute를 제거한다.
-
-
만약 element["
removeAttributes"]가 기본값과 함께 « »로 간주될 때 포함한다면 attribute를:-
Remove를 호출해 element["
removeAttributes"]에서 attribute를 제거한다.
-
-
-
-
Append를 호출해 attribute를 configuration["
removeAttributes"]에 추가한다. -
true를 반환한다.
-
SanitizerConfig
configuration에서 수행하려면,
다음을 실행한다:
참고: 이 알고리즘의 이름은 remove unsafe이지만, 우리는 이 명세의 의미에서 엄격하게 "unsafe"라는 용어를 사용하여, 문서에 삽입될 때 JavaScript가 실행될 수 있는 콘텐츠를 지칭한다. 다시 말해, 이 메서드는 XSS의 기회를 제거한다.
-
단언: 키 집합이 내장 안전 기준 구성의 동일함: «[ "
removeElements", "removeAttributes" ] ». -
result를 false로 둔다.
-
각 element에 대하여, 내장 안전 기준 구성[
removeElements]의 항목을 순회한다:-
요소를 제거를 호출해 configuration에서 element를 제거한다.
-
호출 결과가 true이면, result를 true로 설정한다.
-
-
각 attribute에 대하여, 내장 안전 기준 구성[
removeAttributes]의 항목을 순회한다:-
속성을 제거를 호출해 configuration에서 attribute를 제거한다.
-
호출 결과가 true이면, result를 true로 설정한다.
-
-
각 attribute에 대하여, 이벤트 핸들러 콘텐츠 속성에 나열된 항목을 순회한다:
-
속성을 제거를 호출해 configuration에서 attribute를 제거한다.
-
호출 결과가 true이면, result를 true로 설정한다.
-
-
result를 반환한다.
3.3. 구성 설정
Sanitizer
sanitizer가 주어진다:
-
구성 정규화 configuration과 allowCommentsAndDataAttributes를 사용해 수행.
-
configuration이 유효하지 않으면 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와 함께
구성을 정규화(canonicalize)하려면:
참고: configuration이 JavaScript 값을
SanitizerConfig(으)로
변환한 [WebIDL]의 결과라고 가정한다.
-
configuration["
elements"] 와 configuration["removeElements"]가 모두 존재하지 않으면, set으로 configuration["removeElements"]를 « »로 설정한다. -
configuration["
attributes"] 와 configuration["removeAttributes"]가 모두 존재하지 않으면, set으로 configuration["removeAttributes"]를 « »로 설정한다. -
configuration["
elements"]가 존재한다면:-
elements를 « »로 둔다.
-
각 configuration["
elements"]의 element에 대하여 다음을 수행한다:-
속성이 있는 Sanitizer 요소 정규화의 결과 element를 Append로 elements에 추가한다.
-
-
configuration["
elements"]를 elements로 설정한다.
-
-
configuration["
removeElements"]가 존재한다면:-
elements를 « »로 둔다.
-
각 configuration["
removeElements"]의 element에 대하여 다음을 수행한다:-
Sanitizer 요소 정규화의 결과 element를 Append로 elements에 추가한다.
-
-
configuration["
removeElements"]를 elements로 설정한다.
-
-
configuration["
replaceWithChildrenElements"]가 존재한다면:-
elements를 « »로 둔다.
-
각 configuration["
replaceWithChildrenElements"]의 element에 대하여 다음을 수행한다:-
Sanitizer 요소 정규화의 결과 element를 Append로 elements에 추가한다.
-
-
configuration["
replaceWithChildrenElements"]를 elements로 설정한다.
-
-
configuration["
attributes"]가 존재한다면:-
attributes를 « »로 둔다.
-
각 configuration["
attributes"]의 attribute에 대하여 다음을 수행한다:-
Sanitizer 속성 정규화의 결과 attribute를 Append로 attributes에 추가한다.
-
-
configuration["
attributes"]를 attributes로 설정한다.
-
-
configuration["
removeAttributes"]가 존재한다면:-
attributes를 « »로 둔다.
-
각 configuration["
removeAttributes"]의 attribute에 대하여 다음을 수행한다:-
Sanitizer 속성 정규화의 결과 attribute를 Append로 attributes에 추가한다.
-
-
configuration["
removeAttributes"]를 attributes로 설정한다.
-
-
configuration["
comments"]가 존재하지 않으면, set으로 configuration["comments"]를 allowCommentsAndDataAttributes로 설정한다. -
configuration["
attributes"]가 존재하고, configuration["dataAttributes"]가 존재하지 않으면, set으로 configuration["dataAttributes"]를 allowCommentsAndDataAttributes로 설정한다.
SanitizerElementWithAttributes
element에 대해
속성이 있는 Sanitizer 요소를 정규화하려면:
-
result를 Sanitizer 요소 정규화를 element에 대해 수행한 결과로 둔다.
-
element가 dictionary라면:
-
element["
attributes"]가 존재한다면:-
attributes를 « »로 둔다.
-
각 element["
attributes"]의 attribute에 대하여:-
Sanitizer 속성 정규화를 attribute에 대해 수행한 결과를 Append로 attributes에 추가한다.
-
-
Set으로 result["
attributes"]를 attributes로 설정한다.
-
-
element["
removeAttributes"]가 존재한다면:-
attributes를 « »로 둔다.
-
각 element["
removeAttributes"]의 attribute에 대하여:-
Sanitizer 속성 정규화를 attribute에 대해 수행한 결과를 Append로 attributes에 추가한다.
-
-
Set으로 result["
removeAttributes"]를 attributes로 설정한다.
-
-
-
result["
attributes"] 와 result["removeAttributes"]가 모두 존재하지 않으면:-
Set으로 result["
removeAttributes"]를 « »로 설정한다.
-
-
result를 반환한다.
SanitizerElement
element에 대해
Sanitizer
요소를 정규화하려면,
element와
기본 네임스페이스로 HTML namespace를 사용하여
Sanitizer 이름 정규화의 결과를 반환한다.
SanitizerAttribute
attribute에 대해
Sanitizer
속성을 정규화하려면,
attribute와 기본 네임스페이스로 null을 사용하여
Sanitizer 이름 정규화의 결과를 반환한다.
-
단언: name은
DOMString이거나 dictionary여야 한다. -
만약 name이
DOMString이라면, «[ "name" → name, "namespace" → defaultNamespace]»를 반환한다. -
단언: name은 dictionary이며 name["name"]과 name["namespace"]가 모두 존재한다.
-
만약 name["namespace"]가 빈 문자열이면, 이를 null로 설정한다.
-
«[
"name" → name["name"],
"namespace" → name["namespace"]
]»를 반환한다.
3.5. 지원 알고리즘
이 명세에서 사용하는 정규화된
element
및 attribute name
목록의 경우,
목록 멤버십은 "name"과 "namespace"
항목이 모두 일치하는지에 기반한다:
-
removed를 false로 둔다.
-
list의 각 entry에 대해:
-
item["name"]이 entry["name"]와 같고 item["namespace"]가 entry["namespace"]와 같다면:
-
entry를 list에서 제거한다.
-
removed를 true로 설정한다.
-
-
-
removed를 반환한다.
-
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["namespace"]가 같지 않다면 itemB["namespace"], false를 반환한다.
-
-
itemA["name"]이 code unit less than itemB["name"]인지 반환한다.
-
result를 « »로 둔다.
-
list의 각 entry에 대해, add entry를 result에 추가한다.
-
result를 반환한다.
SanitizerElement를
담고,
set intersection과 동일하되,
set의
엔트리는 미리 정규화되어야 한다:
-
set A를 « [] »로 둔다.
-
set B를 « [] »로 둔다.
-
A의 각 entry에 대해, append로 정규화 entry를 set A에 추가한다.
-
B의 각 entry에 대해, append로 정규화 entry를 set B에 추가한다.
-
set A와 set B의 교집합을 반환한다.
3.6. 내장값
내장값(builtin)은 네 가지가 있다:
내장 안전 기본 구성은 다음과 같다:
{ "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" : "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" : "a" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "href" , "namespace" : null }, { "name" : "hreflang" , "namespace" : null }, { "name" : "type" , "namespace" : null } ] }, { "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:" 탐색이 "unsafe"로 간주되는 내장 navigating URL 속성 목록은 다음과 같다:
«[
[
{ "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" → "input", "namespace" → HTML 네임스페이스
},
{ "name" → "formaction", "namespace" → null }
],
[
{ "name" → "a", "namespace" → SVG 네임스페이스 },
{ "name" → "href", "namespace" → null }
],
[
{ "name" → "a", "namespace" → SVG 네임스페이스 },
{ "name" → "href", "namespace" → XLink 네임스페이스
}
],
]»
내장
animating 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는 주어진 HTML 콘텐츠를 순회하며 구성에 따라 요소와 속성을 제거함으로써 DOM 기반 크로스 사이트 스크립팅(XSS)을 방지하기 위한 것입니다. 이 명세의 API는 스크립트 실행이 가능한 마크업이 남게 되는 Sanitizer 객체의 생성을 지원해서는 안 되며, 그렇게 된다면 이는 위협 모델 상의 버그입니다.
그렇다 하더라도, Sanitizer API의 올바른 사용만으로는 방지할 수 없는 보안 이슈들이 있으며, 이러한 시나리오들이 다음 절에서 설명됩니다.
4.1. 서버 사이드 반사 및 저장 XSS
이 절은 규범(normative)이 아닙니다.
Sanitizer API는 DOM 내에서만 동작하며, 기존 DocumentFragment를 순회 및 필터링하는 기능을 추가합니다. Sanitizer는 서버 측 반사(reflected) 또는 저장(stored) XSS를 다루지 않습니다.
4.2. DOM 클로버링
이 절은 규범(normative)이 아닙니다.
DOM 클로버링은 악의적인 HTML이 id나 name 속성을 통해 요소 이름을 지정함으로써, HTML 요소의 children과
같은 프로퍼티가 악성 콘텐츠에 의해 가려지는 공격을 의미합니다.
Sanitizer API는 기본 상태에서는 DOM 클로버링 공격을 막지 못하지만, id 및 name 속성을 제거하도록 구성할 수 있습니다.
4.3. 스크립트 gadget을 이용한 XSS
이 절은 규범(normative)이 아닙니다.
스크립트 gadget은 공격자가 인기있는 JavaScript 라이브러리의 기존 애플리케이션 코드를 활용하여 자신의 코드를 실행하게 만드는 기법입니다. 종종 무해해 보이는 코드나 inert DOM 노드를 삽입해 프레임워크가 이를 파싱 및 해석하여 JavaScript 실행에 이용하게 하는 방식입니다.
Sanitizer API만으로는 이러한 공격을 막을 수 없으며, 페이지 저자는 일반적으로 알 수 없는 요소를 명시적으로 허용해야 하고, data- 및
slot 속성, <slot>, <template>과 같은 템플릿/프레임워크 전용 마크업에 대해서도 명시적으로
허용/제거 구성을 해야 합니다. 이러한 제한은 완전하지 않으므로, 페이지 저자에게는 사용하는 서드파티 라이브러리의 동작을 면밀히 검토할 것을 권장합니다.
4.4. Mutated XSS
이 절은 규범(normative)이 아닙니다.
Mutated XSS(mXSS)는 HTML 조각을 올바른 컨텍스트 없이 파싱할 때 발생하는 파서 컨텍스트 불일치에 기반한 공격입니다. 특히, 파싱된 HTML 프래그먼트를 문자열로 직렬화한 뒤, 다른 부모 요소에 삽입하여 재파싱하면 파싱 및 해석 결과가 동일하다고 보장할 수 없습니다. 예를 들어 foreign content나 잘못 중첩된 태그의 파싱 동작 변화에 의존하여 공격이 이루어질 수 있습니다.
Sanitizer API는 문자열을 노드 트리로 변환하는 함수만 제공합니다. 컨텍스트는 모든 sanitizer 함수에서 암묵적으로 제공됩니다:
Element.setHTML()은 현재 요소를, Document.parseHTML()은 새 문서를 사용합니다. 따라서 Sanitizer API는
mutated XSS에 직접적으로 영향을 받지 않습니다.
개발자가 정리된(sanitized) 노드 트리를 .innerHTML 등으로 문자열로 변환한 뒤, 이를 다시 파싱한다면 mutated XSS가 발생할 수 있습니다. 이러한
방식은 권장하지 않습니다. HTML을 어쩔 수 없이 문자열로 처리/전달해야 한다면, 해당 문자열은 신뢰할 수 없는 것으로 간주하고 DOM에 삽입할 때 반드시 다시 한 번 정리해야 합니다. 즉,
정리되고 직렬화된 HTML 트리는 더 이상 정리된 것으로 간주할 수 없습니다.
mXSS에 대해 보다 자세한 내용은 [MXSS]를 참고하십시오.
5. 감사의 글
이 작업은 cure53의 [DOMPURIFY],
Internet Explorer의 window.toStaticHTML()
그리고 Ben Bucksch의
[HTMLSanitizer]에서 영감을
얻었습니다. Anne van Kesteren, Krzysztof Kotowicz,
Andrew C. H. Mc Millan, Tom Schuster, Luke Warlow, Guillaume Weghsteen,
그리고 Mike West가 소중한 피드백을 주셨습니다.