HTML Sanitizer API

커뮤니티 그룹 초안 보고서,

이 버전:
https://wicg.github.io/sanitizer-api/
이슈 추적:
GitHub
명세 내 인라인
편집자:
Frederik Braun (Mozilla)
Mario Heiderich (Cure53)
Daniel Vogelheim (Google LLC)

개요

이 문서는 개발자가 신뢰할 수 없는 HTML 입력값을 받아 문서의 DOM에 안전하게 삽입할 수 있도록 정화하는 API 집합을 명세합니다.

이 문서의 상태

이 명세는 웹 플랫폼 인큐베이터 커뮤니티 그룹에서 발행되었습니다. W3C 표준이 아니며, W3C 표준 트랙에도 포함되어 있지 않습니다. W3C 커뮤니티 기여자 라이선스 계약(CLA)에 따라 제한적인 옵트아웃 및 추가 조건이 적용됨을 유의해 주십시오. W3C 커뮤니티 및 비즈니스 그룹에 대해 더 알아보세요.

1. 소개

이 절은 규범적인 내용이 아닙니다.

웹 애플리케이션은 종종 클라이언트 측에서 HTML 문자열을 다루어야 합니다. 예를 들어, 클라이언트 측 템플릿 솔루션의 일부로 사용하거나, 사용자 생성 콘텐츠를 렌더링할 때 등입니다. 이를 안전하게 처리하는 것은 어렵습니다. 문자열을 단순히 이어붙여 ElementinnerHTML 에 넣는 순진한 방식은 다양한 예기치 못한 방식으로 JavaScript가 실행될 위험이 있습니다.

[DOMPURIFY]와 같은 라이브러리는 문자열을 삽입하기 전에 신중하게 파싱하고 정화하여, DOM을 생성한 뒤 허용 목록을 통해 멤버를 필터링함으로써 이러한 문제를 관리하려고 시도합니다. 하지만 웹에 노출된 파싱 API가 실제 브라우저가 "진짜" DOM에 문자열을 렌더링하는 방식과 항상 합리적으로 대응되지 않기 때문에, 이는 취약한 접근 방식임이 입증되었습니다. 또한 라이브러리는 브라우저의 변화하는 동작을 계속 따라가야 하며, 한때 안전했던 것이 새로운 플랫폼 수준의 기능으로 인해 위험이 될 수 있습니다.

브라우저는 언제 코드를 실행할지 꽤 잘 알고 있습니다. 브라우저가 임의의 문자열에서 안전하게 HTML을 렌더링할 수 있도록 가르침으로써 사용자 공간 라이브러리보다 더 나은 방법을 제공할 수 있으며, 브라우저의 파서 구현이 변경될 때마다 함께 유지·업데이트될 가능성도 훨씬 높습니다. 이 문서는 바로 그 목적을 위한 API를 개략적으로 설명합니다.

1.1. 목표

1.2. API 요약

Sanitizer API는 HTML이 포함된 문자열을 파싱하여 DOM 트리로 변환하고, 사용자 정의 설정에 따라 결과 트리를 필터링하는 기능을 제공합니다. 메서드는 두 가지 방식으로 나뉩니다.

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 = {});
};
ElementsetHTMLUnsafe(html, options) 메서드 단계:
  1. compliantHTMLGet Trusted Type compliant string 알고리즘에 TrustedHTML, thisrelevant global object, html, "Element setHTMLUnsafe", 그리고 "script"를 넘겨 호출한 결과로 설정합니다.

  2. targetthistemplate contents를 가지면 해당 값으로, 아니면 this로 설정합니다.

  3. Set and filter HTMLtarget, this, compliantHTML, options, 그리고 false를 넘겨 호출합니다.

ElementsetHTML(html, options) 메서드 단계:
  1. targetthistemplate contents를 가지면 해당 값으로, 아니면 this로 설정합니다.

  2. Set and filter HTMLtarget, 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에도 동일하게 구현되어 있습니다:

ShadowRootsetHTMLUnsafe(html, options) 메서드 단계:
  1. compliantHTMLGet Trusted Type compliant string 알고리즘에 TrustedHTML, thisrelevant global object, html, "ShadowRoot setHTMLUnsafe", 그리고 "script"를 넘겨 호출한 결과로 설정합니다.

  2. Set and filter HTMLthis, thisshadow host (컨텍스트 요소), compliantHTML, options, 그리고 false를 넘겨 호출합니다.

ShadowRootsetHTML(html, options) 메서드 단계:
  1. Set and filter HTMLthis (타깃), 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) 메서드 단계:
  1. compliantHTMLGet Trusted Type compliant string 알고리즘에 TrustedHTML, thisrelevant global object, html, "Document parseHTMLUnsafe", 그리고 "script"를 넘겨 호출한 결과로 설정합니다.

  2. document를 새로운 Document로, content type이 "text/html"로 설정합니다.

    참고: document에는 브라우징 컨텍스트가 없으므로 스크립팅이 비활성화되어 있습니다.

  3. documentallow declarative shadow roots를 true로 설정합니다.

  4. Parse HTML from a stringdocumentcompliantHTML로 호출합니다.

  5. sanitizerget a sanitizer instance from optionsoptions와 false를 넘겨 호출한 결과로 설정합니다.

  6. sanitizedocument, sanitizer, 그리고 false로 호출합니다.

  7. document를 반환합니다.

parseHTML(html, options) 메서드 단계:
  1. document를 새로운 Document로, content type이 "text/html"로 설정합니다.

    참고: document에는 브라우징 컨텍스트가 없으므로 스크립팅이 비활성화되어 있습니다.

  2. documentallow declarative shadow roots를 true로 설정합니다.

  3. Parse HTML from a stringdocumenthtml로 호출합니다.

  4. sanitizerget a sanitizer instance from optionsoptions와 true를 넘겨 호출한 결과로 설정합니다.

  5. sanitizedocument, sanitizer, 그리고 true로 호출합니다.

  6. document를 반환합니다.

2.2. SetHTML 옵션과 설정 객체

setHTML()류의 메서드들은 모두 옵션 딕셔너리를 받습니다. 현재 이 딕셔너리에는 한 가지 멤버만 정의되어 있습니다:

enum SanitizerPresets { "default" };
dictionary SetHTMLOptions {
  (Sanitizer or SanitizerConfig or SanitizerPresets) sanitizer = "default";
};
dictionary SetHTMLUnsafeOptions {
  (Sanitizer or SanitizerConfig or SanitizerPresets) sanitizer = {};
};

Sanitizer 설정 객체는 필터 설정을 캡슐화합니다. 동일한 설정은 "안전" 또는 "비안전" 메서드 모두에 사용할 수 있으며, "안전" 메서드는 전달된 설정에 대해 암묵적으로 removeUnsafe 연산을 수행하며, 설정이 없을 경우 기본 설정을 사용합니다. 의도는 페이지 생명주기 초기에 하나 또는 소수의 설정을 미리 구축해두고 필요할 때마다 재사용하는 것입니다. 이렇게 하면 구현체가 설정을 미리 처리할 수 있습니다.

설정 객체는 설정 딕셔너리를 반환하도록 질의할 수 있으며, 직접 수정할 수도 있습니다.

[Exposed=Window]
interface Sanitizer {
  constructor(optional (SanitizerConfig or SanitizerPresets) configuration = "default");

  // 설정 질의:
  SanitizerConfig get();

  // Sanitizer의 리스트와 필드 수정:
  undefined allowElement(SanitizerElementWithAttributes element);
  undefined removeElement(SanitizerElement element);
  undefined replaceElementWithChildren(SanitizerElement element);
  undefined allowAttribute(SanitizerAttribute attribute);
  undefined removeAttribute(SanitizerAttribute attribute);
  undefined setComments(boolean allow);
  undefined setDataAttributes(boolean allow);

  // 스크립트 실행 마크업 제거. 여러 리스트를 수정할 수 있음:
  undefined removeUnsafe();
};

Sanitizerconfiguration(SanitizerConfig)을 가집니다.

constructor(configuration) 메서드 단계:
  1. 만약 configurationSanitizerPresets 문자열이라면:

    1. Assert: configuration"default"임을 확인합니다.

    2. configuration내장 안전 기본 설정으로 설정합니다.

  2. validset a configurationconfiguration과 true를 넘겨, this에서 호출한 결과로 설정합니다.

  3. 만약 valid가 false라면 TypeError를 던집니다.

get() 메서드 단계는 thisconfiguration 값을 반환합니다.
allowElement(element) 메서드 단계는 요소 허용elementthisconfiguration에 대해 수행합니다.
removeElement(element) 메서드 단계는 요소 제거elementthisconfiguration에 대해 수행합니다.
replaceElementWithChildren(element) 메서드 단계는 요소를 자식으로 대체elementthisconfiguration에 대해 수행합니다.
allowAttribute(attribute) 메서드 단계는 속성 허용attributethisconfiguration에 대해 수행합니다.
removeAttribute(attribute) 메서드 단계는 속성 제거attributethisconfiguration에 대해 수행합니다.
setComments(allow) 메서드 단계는 주석 설정allowthisconfiguration에 대해 수행합니다.
setDataAttributes(allow) 메서드 단계는 data 속성 설정allowthisconfiguration에 대해 수행합니다.
removeUnsafe() 메서드 단계는 thisconfigurationremove unsafe 호출 결과로 갱신합니다.

2.3. 설정 딕셔너리

dictionary SanitizerElementNamespace {
  required DOMString name;
  DOMString? _namespace = "http://www.w3.org/1999/xhtml";
};

// "elements"에서 사용됨
dictionary SanitizerElementNamespaceWithAttributes : SanitizerElementNamespace {
  sequence<SanitizerAttribute> attributes;
  sequence<SanitizerAttribute> removeAttributes;
};

typedef (DOMString or SanitizerElementNamespace) SanitizerElement;
typedef (DOMString or SanitizerElementNamespaceWithAttributes) SanitizerElementWithAttributes;

dictionary SanitizerAttributeNamespace {
  required DOMString name;
  DOMString? _namespace = null;
};
typedef (DOMString or SanitizerAttributeNamespace) SanitizerAttribute;

dictionary SanitizerConfig {
  sequence<SanitizerElementWithAttributes> elements;
  sequence<SanitizerElement> removeElements;
  sequence<SanitizerElement> replaceWithChildrenElements;

  sequence<SanitizerAttribute> attributes;
  sequence<SanitizerAttribute> removeAttributes;

  boolean comments;
  boolean dataAttributes;
};

3. 알고리즘

HTML 설정 및 필터링을 하려면, Element 또는 DocumentFragment target, Element contextElement, 문자열 html, 딕셔너리 options, boolean safe이 주어졌을 때:
  1. safe가 참이고 contextElementlocal name이 "script"이며 contextElementnamespaceHTML 네임스페이스 또는 SVG 네임스페이스라면, 반환한다.

  2. sanitizer옵션에서 sanitizer 인스턴스 얻기optionssafe로 호출한 결과로 설정한다.

  3. newChildrenHTML 프래그먼트 파싱 알고리즘contextElement, html, 그리고 true를 넘겨 호출한 결과로 설정한다.

  4. fragment를 새로운 DocumentFragment 로, node documentcontextElementnode document인 것으로 생성한다.

  5. nodenewChildren에서 fragment에 추가한다.

  6. sanitizefragmentsanitizer, safe로 호출한다.

  7. 모두 대체fragmenttarget 내에서 실행한다.

옵션에서 sanitizer 인스턴스 얻기딕셔너리 optionsboolean safe로 하면:

참고: 이 알고리즘은 SetHTMLOptionsSetHTMLUnsafeOptions 모두에 적용된다. 기본값만 다르다.

  1. sanitizerSpec을 "default"로 설정한다.

  2. 만약 options["sanitizer"] 존재한다면:

    1. sanitizerSpecoptions["sanitizer"]로 설정한다.

  3. Assert: sanitizerSpecSanitizer 인스턴스이거나, 문자열로서 SanitizerPresets 멤버이거나, 딕셔너리여야 한다.

  4. 만약 sanitizerSpec문자열이라면:

    1. Assert: sanitizerSpec"default"임을 확인한다.

    2. sanitizerSpec내장 안전 기본 설정으로 설정한다.

  5. Assert: sanitizerSpecSanitizer 인스턴스이거나, 딕셔너리이어야 한다.

  6. 만약 sanitizerSpec딕셔너리라면:

    1. sanitizer를 새로운 Sanitizer 인스턴스로 설정한다.

    2. setConfigurationResult설정 적용sanitizerSpecnot safe를 넘겨 sanitizer에서 호출한 결과로 설정한다.

    3. setConfigurationResult가 false라면, TypeError를 던진다.

    4. sanitizerSpecsanitizer로 설정한다.

  7. Assert: sanitizerSpecSanitizer 인스턴스여야 한다.

  8. sanitizerSpec을 반환한다.

3.1. 정화 알고리즘

주요 sanitize 연산에서, ParentNode node, Sanitizer sanitizer, boolean safe를 사용하여 다음 단계를 실행한다:
  1. configurationsanitizerconfiguration 값으로 설정한다.

  2. safe가 true라면, configurationremove unsafeconfiguration에 호출한 결과로 설정한다.

  3. sanitize corenode, configuration, handleJavascriptNavigationUrlssafe로 하여 호출한다.

sanitize core 연산은, ParentNode node, SanitizerConfig configuration, boolean handleJavascriptNavigationUrls를 사용하며, DOM 트리를 node에서 시작하여 순회하고, 일부 특수한 경우(예: template contents)를 재귀적으로 처리할 수 있다. 단계는 다음과 같다:
  1. childnodechildren에서 반복한다:

    1. Assert: child구현하는 것이 Text, Comment, Element, 또는 DocumentType이어야 한다.

      참고: 현재 이 알고리즘은 HTML 파서의 결과에만 호출되며, 위의 assert가 항상 만족된다. DocumentTypeparseHTMLparseHTMLUnsafe에서만 발생해야 한다. 만약 앞으로 이 알고리즘이 다른 문맥에서 사용된다면, 이 가정을 재검토해야 한다.

    2. child구현하는 것이 DocumentType이면, continue한다.

    3. child구현하는 것이 Text이면, continue한다.

    4. child구현하는 것이 Comment라면:

      1. configuration["comments"] 가 true가 아니면, child를 제거한다.

    5. 그 외에는:

      1. elementNameSanitizerElementNamespace로, childlocal namenamespace를 사용하여 만든다.

      2. configuration["replaceWithChildrenElements"] 가 elementName을 포함하면:

        1. sanitize corechild, configuration, handleJavascriptNavigationUrls로 호출한다.

        2. replace allchildchildren에 대해 child 내에서 실행한다.

        3. continue한다.

      3. configuration["removeElements"] 가 elementName을 포함하거나, configuration["elements"] 가 비어있지 않으면서 elementName을 포함하지 않으면:

        1. child를 제거한다.

        2. continue한다.

      4. elementName이 «[ "name" → "template", "namespace" → HTML namespace ]»와 동일하면

        1. sanitize corechildtemplate contentsconfiguration, handleJavascriptNavigationUrls로 호출한다.

      5. childshadow host라면, sanitize corechildshadow rootconfiguration, handleJavascriptNavigationUrls로 호출한다.

      6. attributechildattribute list에서 반복한다:

        1. attrNameSanitizerAttributeNamespace로, attributelocal namenamespace로 만든다.

        2. configuration["removeAttributes"] 가 attrName을 포함하면, child에서 attribute를 제거한다.

        3. configuration["elements"]["removeAttributes"] 가 attrName을 포함하면, child에서 attribute를 제거한다.

        4. 다음 조건이 모두 false라면 child에서 attribute를 제거한다.

        5. handleJavascriptNavigationUrls가 참이라면:

          1. «[elementName, attrName]»이 내장 네비게이팅 URL 속성 리스트에 일치하고, attributejavascript: URL을 포함하면, child에서 attribute를 제거한다.

          2. childnamespaceMathML Namespace이고, attrlocal name이 "href"이며, attrnamespacenull 또는 XLink namespace이고, attrjavascript: URL을 포함하면, attr를 제거 한다.

          3. 내장 애니메이팅 URL 속성 리스트 가 «[elementName, attrName]»을 포함하고, attr이 "href" 또는 "xlink:href"라면, attr를 제거 한다.

      7. sanitize corechildconfiguration, handleJavascriptNavigationUrls로 호출한다.

참고: 현재 브라우저는 javascript: URL을 오직 네비게이션 시에만 지원한다. 네비게이션 자체는 XSS 위협이 아니므로 javascript: URL로의 네비게이션만 처리하며, 일반적인 네비게이션은 처리하지 않는다.

선언적 네비게이션은 몇 가지 범주로 나뉜다:

  1. 앵커 요소. (HTML 및 SVG 네임스페이스의 <a>)

  2. 폼 요소(폼 action을 통한 네비게이션).

  3. [MathML]모든 요소를 앵커로 동작시키는 것을 허용한다.

  4. [SVG11] 애니메이션.

처음 두 가지는 내장 네비게이팅 URL 속성 리스트에서 다뤄진다.

MathML의 경우, 이 명세에 "네임스페이스별 글로벌" 규칙이 없으므로 별도의 규칙으로 처리된다.

SVG 애니메이션의 경우는 내장 애니메이팅 URL 속성 리스트에서 다뤄진다. 하지만 SVG 애니메이션 요소의 해석이 최종 타겟에 의존하며, 정화 중에는 최종 타겟을 알 수 없으므로 sanitize 알고리즘은 href 속성의 모든 애니메이션을 차단한다.

attributejavascript: URL을 포함하는지 판단하려면:
  1. url기본 URL 파서attribute을 넘겨 실행한 결과로 설정한다.

  2. urlfailure이면, false를 반환한다.

  3. urlscheme이 "javascript"인지 반환한다.

3.2. 설정 처리

요소 허용 elementSanitizerConfig configuration에 적용하려면, 다음을 수행한다:
  1. element요소 속성 표준화element에 적용한 결과로 설정한다.

  2. 제거 elementconfiguration["elements"]에서 제거한다.

  3. 추가 elementconfiguration["elements"]에 추가한다.

  4. 제거 elementconfiguration["removeElements"]에서 제거한다.

  5. 제거 elementconfiguration["replaceWithChildrenElements"]에서 제거한다.

참고: allowElement 처리는 다른 메서드보다 복잡하다. 요소 허용 리스트에 요소별 허용/제거 속성 리스트가 있을 수 있다. 먼저 주어진 요소를 리스트에서 제거한 후 다시 추가하는데, 이는 병합이나 다른 방식이 아닌 전달된 리스트로 완전히 재설정하는 효과가 있다. 즉, 요소별 허용/제거 리스트는 전체로만 설정된다.

참고: 제거는 이름과 네임스페이스로 매칭된다. 속성이 추가된 요소를 추가하더라도 removeElementsreplaceWithChildrenElements 리스트에서 동일 요소를 제거한다.

요소 제거 elementSanitizerConfig configuration에서 제거하려면:
  1. element요소 표준화element에 적용한 결과로 설정한다.

  2. 추가 elementconfiguration["removeElements"]에 추가한다.

  3. 제거 elementconfiguration["elements"] 리스트에서 제거한다.

  4. 제거 elementconfiguration["replaceWithChildrenElements"]에서 제거한다.

요소를 자식으로 대체 elementSanitizerConfig configuration에 적용하려면:
  1. element요소 표준화element에 적용한 결과로 설정한다.

  2. 추가 elementconfiguration["replaceWithChildrenElements"]에 추가한다.

  3. 제거 elementconfiguration["removeElements"]에서 제거한다.

  4. 제거 elementconfiguration["elements"] 리스트에서 제거한다.

속성 허용 attributeSanitizerConfig configuration에 적용하려면:
  1. attribute속성 표준화attribute에 적용한 결과로 설정한다.

  2. 추가 attributeconfiguration["attributes"]에 추가한다.

  3. 제거 attributeconfiguration["removeAttributes"]에서 제거한다.

속성 제거 attributeSanitizerConfig configuration에서 제거하려면:
  1. attribute속성 표준화attribute에 적용한 결과로 설정한다.

  2. 추가 attributeconfiguration["removeAttributes"]에 추가한다.

  3. 제거 attributeconfiguration["attributes"]에서 제거한다.

주석 설정allowSanitizerConfig configuration에 적용하려면:
  1. configuration["comments"] 를 allow로 설정한다.

data 속성 설정allowSanitizerConfig configuration에 적용하려면:
  1. configuration["dataAttributes"] 를 allow로 설정한다.

참고: 이 알고리즘은 remove unsafe라 불리지만, 이 명세에서 "unsafe"는 JavaScript가 문서에 삽입될 때 실행되는 콘텐츠를 의미한다. 즉, 이 메서드는 XSS 위험을 제거한다.

unsafe 제거configuration에서 수행하려면:

  1. Assert: 내장 안전 기준 설정에는 removeElementsremoveAttributes 키가 설정돼 있지만, elements, replaceWithChildrenElements, attributes는 없다.

  2. resultconfiguration의 복사본으로 설정한다.

  3. element내장 안전 기준 설정[removeElements]에서 반복한다:

    1. 요소 제거element, result에 대해 호출한다.

  4. attribute내장 안전 기준 설정[removeAttributes]에서 반복한다:

    1. 속성 제거attribute, result에 대해 호출한다.

  5. attribute이벤트 핸들러 콘텐츠 속성에서 반복한다:

    1. 속성 제거attribute, result에 대해 호출한다.

  6. result를 반환한다.

설정 적용딕셔너리 configuration, boolean allowCommentsAndDataAttributes, Sanitizer sanitizer에 적용하려면:
  1. elementconfiguration["elements"] 에서 반복한다:

    1. 요소 허용element, sanitizerconfiguration에 대해 호출한다.

  2. elementconfiguration["removeElements"] 에서 반복한다:

    1. 요소 제거element, sanitizerconfiguration에 대해 호출한다.

  3. elementconfiguration["replaceWithChildrenElements"] 에서 반복한다:

    1. 요소를 자식으로 대체element, sanitizerconfiguration에 대해 호출한다.

  4. attributeconfiguration["attributes"] 에서 반복한다:

    1. 속성 허용attribute, sanitizerconfiguration에 대해 호출한다.

  5. attributeconfiguration["removeAttributes"] 에서 반복한다:

    1. 속성 제거attribute, sanitizerconfiguration에 대해 호출한다.

  6. configuration["comments"] 가 존재하면:

    1. 주석 설정configuration["comments"] 과 sanitizerconfiguration에 대해 호출한다.

    2. 그 외에는 주석 설정allowCommentsAndDataAttributessanitizerconfiguration에 대해 호출한다.

  7. configuration["dataAttributes"] 가 존재하면:

    1. data 속성 설정configuration["dataAttributes"] 와 sanitizerconfiguration에 대해 호출한다.

    2. 그 외에는 data 속성 설정allowCommentsAndDataAttributessanitizerconfiguration에 대해 호출한다.

  8. 다음 조건이 모두 true면 true를 반환한다:

참고: 이전 버전 명세에서는 설정 표준화가 복잡하게 정의돼 있었으나, 이제 각 메서드 정의에서 이 과정을 처리한다.

참고: 이 연산은 Sanitizer 조작 메서드로 정의된다. 이 메서드들은 다른 리스트에서 일치하는 항목을 제거한다. 마지막 단계의 크기 비교로 이를 확인한다. 예시: { allow: ["div", "div"] }은 허용 리스트에 하나의 요소만 들어가게 되고, 최종 테스트에서 false가 되어 호출자가 예외를 던지게 된다.

요소별 속성 리스트와 문법 오류에 대한 에러 체크가 아직 부족함.

요소 속성 표준화 SanitizerElementWithAttributes element에 대해 다음을 수행한다:
  1. result요소 표준화element에 적용한 결과로 설정한다.

  2. element딕셔너리라면:

    1. attributeelement["attributes"]에서 반복한다:

      1. 추가 속성 표준화attribute에 적용한 결과를 result["attributes"]에 추가한다.

    2. attributeelement["removeAttributes"]에서 반복한다:

      1. 추가 속성 표준화attribute에 적용한 결과를 result["removeAttributes"]에 추가한다.

  3. result를 반환한다.

요소 표준화 SanitizerElement element에 대해, 이름 표준화elementHTML 네임스페이스를 기본 네임스페이스로 하여 적용한 결과를 반환한다.
속성 표준화 SanitizerAttribute attribute에 대해, 이름 표준화attribute와 null을 기본 네임스페이스로 하여 적용한 결과를 반환한다.
이름 표준화 name과 기본 네임스페이스 defaultNamespace에 대해 다음 단계를 실행한다:
  1. Assert: nameDOMString 또는 딕셔너리이어야 한다.

  2. nameDOMString이면, «[ "name" → name, "namespace" → defaultNamespace]»를 반환한다.

  3. Assert: name딕셔너리이고 name["name"]이 존재한다.

  4. namespacename["namespace"]가 존재하면 그 값으로, 아니면 defaultNamespace로 설정한다.

  5. namespace가 빈 문자열이면 null로 설정한다.

  6. «[
    "name" → name["name"],
    "namespace" → namespace
    ]»를 반환한다.

3.3. 지원 알고리즘

이 명세에서 사용하는 정규화된 요소속성 이름 리스트의 멤버십은 "name"과 "namespace" 항목이 모두 일치하는지에 기반한다:

Sanitizer 이름 list포함한다는 것은, list순서가 있는 맵entry가 존재하고, item["name"]이 동일하며 entry["name"]도 같고, item["namespace"]가 동일하며 entry["namespace"]도 같은 경우다.
제거itemlist에서, 순서가 있는 맵으로, 모든 entry를 제거한다. 조건: item["name"]이 동일하고 entry["name"]도 같으며 item["namespace"]가 동일하고 entry["namespace"]도 같은 경우.
추가namelist에, name정규화된 형태이고 list순서가 있는 맵일 때:
  1. list포함 name이면, return.

  2. 추가 namelist에 추가한다.

순서가 있는 집합의 동등성은 멤버의 동등성(순서 무관)이다: 순서가 있는 집합 AB동일하려면 AB의 슈퍼셋이고 BA의 슈퍼셋이어야 한다.
boolean boolnot 판단은, bool이 true면 false를 반환하고, 그렇지 않으면 true를 반환한다.

3.4. 내장 기능

내장 기능은 4가지가 있다:

내장 안전 기본 설정은 다음과 같다:

{
  "elements": [
    {
      "name": "html",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "head",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "title",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "body",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "article",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "section",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "nav",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "aside",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "h1",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "h2",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "h3",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "h4",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "h5",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "h6",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "hgroup",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "header",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "footer",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "address",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "p",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "hr",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "pre",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "blockquote",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": [
        {
          "name": "cite",
          "namespace": null
        }
      ]
    },
    {
      "name": "ol",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": [
        {
          "name": "reversed",
          "namespace": null
        },
        {
          "name": "start",
          "namespace": null
        },
        {
          "name": "type",
          "namespace": null
        }
      ]
    },
    {
      "name": "ul",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "menu",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "li",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": [
        {
          "name": "value",
          "namespace": null
        }
      ]
    },
    {
      "name": "dl",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "dt",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "dd",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "figure",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "figcaption",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "main",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "search",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "div",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "a",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": [
        {
          "name": "href",
          "namespace": null
        },
        {
          "name": "rel",
          "namespace": null
        },
        {
          "name": "hreflang",
          "namespace": null
        },
        {
          "name": "type",
          "namespace": null
        }
      ]
    },
    {
      "name": "em",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "strong",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "small",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "s",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "cite",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "q",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "dfn",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "abbr",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "ruby",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "rt",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "rp",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "data",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": [
        {
          "name": "value",
          "namespace": null
        }
      ]
    },
    {
      "name": "time",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": [
        {
          "name": "datetime",
          "namespace": null
        }
      ]
    },
    {
      "name": "code",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "var",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "samp",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "kbd",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "sub",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "sup",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "i",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "b",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "u",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "mark",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "bdi",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "bdo",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "span",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "br",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "wbr",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "ins",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": [
        {
          "name": "cite",
          "namespace": null
        },
        {
          "name": "datetime",
          "namespace": null
        }
      ]
    },
    {
      "name": "del",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": [
        {
          "name": "cite",
          "namespace": null
        },
        {
          "name": "datetime",
          "namespace": null
        }
      ]
    },
    {
      "name": "table",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "caption",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "colgroup",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": [
        {
          "name": "span",
          "namespace": null
        }
      ]
    },
    {
      "name": "col",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": [
        {
          "name": "span",
          "namespace": null
        }
      ]
    },
    {
      "name": "tbody",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "thead",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "tfoot",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "tr",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": []
    },
    {
      "name": "td",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": [
        {
          "name": "colspan",
          "namespace": null
        },
        {
          "name": "rowspan",
          "namespace": null
        },
        {
          "name": "headers",
          "namespace": null
        }
      ]
    },
    {
      "name": "th",
      "namespace": "http://www.w3.org/1999/xhtml",
      "attributes": [
        {
          "name": "colspan",
          "namespace": null
        },
        {
          "name": "rowspan",
          "namespace": null
        },
        {
          "name": "headers",
          "namespace": null
        },
        {
          "name": "scope",
          "namespace": null
        },
        {
          "name": "abbr",
          "namespace": null
        }
      ]
    },
    {
      "name": "math",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "merror",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mfrac",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mi",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mmultiscripts",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mn",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mo",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": [
        {
          "name": "form",
          "namespace": null
        },
        {
          "name": "fence",
          "namespace": null
        },
        {
          "name": "separator",
          "namespace": null
        },
        {
          "name": "lspace",
          "namespace": null
        },
        {
          "name": "rspace",
          "namespace": null
        },
        {
          "name": "stretchy",
          "namespace": null
        },
        {
          "name": "symmetric",
          "namespace": null
        },
        {
          "name": "maxsize",
          "namespace": null
        },
        {
          "name": "minsize",
          "namespace": null
        },
        {
          "name": "largeop",
          "namespace": null
        },
        {
          "name": "movablelimits",
          "namespace": null
        }
      ]
    },
    {
      "name": "mover",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": [
        {
          "name": "accent",
          "namespace": null
        }
      ]
    },
    {
      "name": "mpadded",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": [
        {
          "name": "width",
          "namespace": null
        },
        {
          "name": "height",
          "namespace": null
        },
        {
          "name": "depth",
          "namespace": null
        },
        {
          "name": "lspace",
          "namespace": null
        },
        {
          "name": "voffset",
          "namespace": null
        }
      ]
    },
    {
      "name": "mphantom",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mprescripts",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mroot",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mrow",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "ms",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mspace",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": [
        {
          "name": "width",
          "namespace": null
        },
        {
          "name": "height",
          "namespace": null
        },
        {
          "name": "depth",
          "namespace": null
        }
      ]
    },
    {
      "name": "msqrt",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mstyle",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "msub",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "msubsup",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "msup",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mtable",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mtd",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": [
        {
          "name": "columnspan",
          "namespace": null
        },
        {
          "name": "rowspan",
          "namespace": null
        }
      ]
    },
    {
      "name": "mtext",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "mtr",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "munder",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": [
        {
          "name": "accentunder",
          "namespace": null
        }
      ]
    },
    {
      "name": "munderover",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": [
        {
          "name": "accent",
          "namespace": null
        },
        {
          "name": "accentunder",
          "namespace": null
        }
      ]
    },
    {
      "name": "semantics",
      "namespace": "http://www.w3.org/1998/Math/MathML",
      "attributes": []
    },
    {
      "name": "svg",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "viewBox",
          "namespace": null
        },
        {
          "name": "preserveAspectRatio",
          "namespace": null
        },
        {
          "name": "height",
          "namespace": null
        },
        {
          "name": "width",
          "namespace": null
        },
        {
          "name": "x",
          "namespace": null
        },
        {
          "name": "y",
          "namespace": null
        }
      ]
    },
    {
      "name": "g",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": []
    },
    {
      "name": "defs",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": []
    },
    {
      "name": "title",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": []
    },
    {
      "name": "desc",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": []
    },
    {
      "name": "metadata",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": []
    },
    {
      "name": "path",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "pathLength",
          "namespace": null
        },
        {
          "name": "d",
          "namespace": null
        }
      ]
    },
    {
      "name": "rect",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "pathLength",
          "namespace": null
        },
        {
          "name": "x",
          "namespace": null
        },
        {
          "name": "y",
          "namespace": null
        },
        {
          "name": "width",
          "namespace": null
        },
        {
          "name": "height",
          "namespace": null
        },
        {
          "name": "rx",
          "namespace": null
        },
        {
          "name": "ry",
          "namespace": null
        }
      ]
    },
    {
      "name": "circle",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "pathLength",
          "namespace": null
        },
        {
          "name": "cx",
          "namespace": null
        },
        {
          "name": "cy",
          "namespace": null
        },
        {
          "name": "r",
          "namespace": null
        }
      ]
    },
    {
      "name": "ellipse",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "pathLength",
          "namespace": null
        },
        {
          "name": "cx",
          "namespace": null
        },
        {
          "name": "cy",
          "namespace": null
        },
        {
          "name": "rx",
          "namespace": null
        },
        {
          "name": "ry",
          "namespace": null
        }
      ]
    },
    {
      "name": "line",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "pathLength",
          "namespace": null
        },
        {
          "name": "x1",
          "namespace": null
        },
        {
          "name": "y1",
          "namespace": null
        },
        {
          "name": "x2",
          "namespace": null
        },
        {
          "name": "y2",
          "namespace": null
        }
      ]
    },
    {
      "name": "polyline",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "pathLength",
          "namespace": null
        },
        {
          "name": "points",
          "namespace": null
        }
      ]
    },
    {
      "name": "polygon",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "pathLength",
          "namespace": null
        },
        {
          "name": "points",
          "namespace": null
        }
      ]
    },
    {
      "name": "text",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "lengthAdjust",
          "namespace": null
        },
        {
          "name": "x",
          "namespace": null
        },
        {
          "name": "y",
          "namespace": null
        },
        {
          "name": "dx",
          "namespace": null
        },
        {
          "name": "dy",
          "namespace": null
        },
        {
          "name": "rotate",
          "namespace": null
        },
        {
          "name": "textLength",
          "namespace": null
        }
      ]
    },
    {
      "name": "tspan",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "lengthAdjust",
          "namespace": null
        },
        {
          "name": "x",
          "namespace": null
        },
        {
          "name": "y",
          "namespace": null
        },
        {
          "name": "dx",
          "namespace": null
        },
        {
          "name": "dy",
          "namespace": null
        },
        {
          "name": "rotate",
          "namespace": null
        },
        {
          "name": "textLength",
          "namespace": null
        }
      ]
    },
    {
      "name": "textPath",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "lengthAdjust",
          "namespace": null
        },
        {
          "name": "textLength",
          "namespace": null
        },
        {
          "name": "path",
          "namespace": null
        },
        {
          "name": "startOffset",
          "namespace": null
        },
        {
          "name": "method",
          "namespace": null
        },
        {
          "name": "spacing",
          "namespace": null
        },
        {
          "name": "side",
          "namespace": null
        }
      ]
    },
    {
      "name": "foreignObject",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "x",
          "namespace": null
        },
        {
          "name": "y",
          "namespace": null
        },
        {
          "name": "width",
          "namespace": null
        },
        {
          "name": "height",
          "namespace": null
        }
      ]
    },
    {
      "name": "marker",
      "namespace": "http://www.w3.org/2000/svg",
      "attributes": [
        {
          "name": "viewBox",
          "namespace": null
        },
        {
          "name": "preserveAspectRatio",
          "namespace": null
        },
        {
          "name": "refX",
          "namespace": null
        },
        {
          "name": "refY",
          "namespace": null
        },
        {
          "name": "markerUnits",
          "namespace": null
        },
        {
          "name": "markerWidth",
          "namespace": null
        },
        {
          "name": "markerHeight",
          "namespace": null
        },
        {
          "name": "orient",
          "namespace": null
        }
      ]
    }
  ],
  "attributes": [
    {
      "name": "dir",
      "namespace": null
    },
    {
      "name": "lang",
      "namespace": null
    },
    {
      "name": "title",
      "namespace": null
    },
    {
      "name": "displaystyle",
      "namespace": null
    },
    {
      "name": "mathbackground",
      "namespace": null
    },
    {
      "name": "mathcolor",
      "namespace": null
    },
    {
      "name": "mathsize",
      "namespace": null
    },
    {
      "name": "scriptlevel",
      "namespace": null
    },
    {
      "name": "fill",
      "namespace": null
    },
    {
      "name": "transform",
      "namespace": null
    },
    {
      "name": "alignment-baseline",
      "namespace": null
    },
    {
      "name": "baseline-shift",
      "namespace": null
    },
    {
      "name": "clip-path",
      "namespace": null
    },
    {
      "name": "clip-rule",
      "namespace": null
    },
    {
      "name": "color",
      "namespace": null
    },
    {
      "name": "color-interpolation",
      "namespace": null
    },
    {
      "name": "cursor",
      "namespace": null
    },
    {
      "name": "direction",
      "namespace": null
    },
    {
      "name": "display",
      "namespace": null
    },
    {
      "name": "dominant-baseline",
      "namespace": null
    },
    {
      "name": "fill-opacity",
      "namespace": null
    },
    {
      "name": "fill-rule",
      "namespace": null
    },
    {
      "name": "font-family",
      "namespace": null
    },
    {
      "name": "font-size",
      "namespace": null
    },
    {
      "name": "font-size-adjust",
      "namespace": null
    },
    {
      "name": "font-stretch",
      "namespace": null
    },
    {
      "name": "font-style",
      "namespace": null
    },
    {
      "name": "font-variant",
      "namespace": null
    },
    {
      "name": "font-weight",
      "namespace": null
    },
    {
      "name": "letter-spacing",
      "namespace": null
    },
    {
      "name": "marker-end",
      "namespace": null
    },
    {
      "name": "marker-mid",
      "namespace": null
    },
    {
      "name": "marker-start",
      "namespace": null
    },
    {
      "name": "opacity",
      "namespace": null
    },
    {
      "name": "paint-order",
      "namespace": null
    },
    {
      "name": "pointer-events",
      "namespace": null
    },
    {
      "name": "shape-rendering",
      "namespace": null
    },
    {
      "name": "stop-color",
      "namespace": null
    },
    {
      "name": "stop-opacity",
      "namespace": null
    },
    {
      "name": "stroke",
      "namespace": null
    },
    {
      "name": "stroke-dasharray",
      "namespace": null
    },
    {
      "name": "stroke-dashoffset",
      "namespace": null
    },
    {
      "name": "stroke-linecap",
      "namespace": null
    },
    {
      "name": "stroke-linejoin",
      "namespace": null
    },
    {
      "name": "stroke-miterlimit",
      "namespace": null
    },
    {
      "name": "stroke-opacity",
      "namespace": null
    },
    {
      "name": "stroke-width",
      "namespace": null
    },
    {
      "name": "text-anchor",
      "namespace": null
    },
    {
      "name": "text-decoration",
      "namespace": null
    },
    {
      "name": "text-overflow",
      "namespace": null
    },
    {
      "name": "text-rendering",
      "namespace": null
    },
    {
      "name": "transform-origin",
      "namespace": null
    },
    {
      "name": "unicode-bidi",
      "namespace": null
    },
    {
      "name": "vector-effect",
      "namespace": null
    },
    {
      "name": "visibility",
      "namespace": null
    },
    {
      "name": "white-space",
      "namespace": null
    },
    {
      "name": "word-spacing",
      "namespace": null
    },
    {
      "name": "writing-mode",
      "namespace": null
    }
  ],
  "comments": false,
  "dataAttributes": false
}

참고: 포함된 [MathML] 마크업은 [SafeMathML]을 기반으로 합니다.

내장 안전 기준 설정은 스크립트 콘텐츠만 차단하는 것을 목적으로 합니다. 다음과 같습니다:

{
  "removeElements": [
    {
      "namespace": "http://www.w3.org/1999/xhtml",
      "name": "script"
    },
    {
      "namespace": "http://www.w3.org/1999/xhtml",
      "name": "frame"
    },
    {
      "namespace": "http://www.w3.org/1999/xhtml",
      "name": "iframe"
    },
    {
      "namespace": "http://www.w3.org/1999/xhtml",
      "name": "object"
    },
    {
      "namespace": "http://www.w3.org/1999/xhtml",
      "name": "embed"
    },
    {
      "namespace": "http://www.w3.org/2000/svg",
      "name": "script"
    },
    {
      "namespace": "http://www.w3.org/2000/svg",
      "name": "use"
    }
  ],
  "removeAttributes": []
}

경고: remove unsafe 알고리즘은 추가적으로 이벤트 핸들러 콘텐츠 속성을 제거하도록 지정합니다. 해당 속성은 [HTML]에서 정의됩니다. 만약 사용자 에이전트[HTML] 명세에 추가적인 이벤트 핸들러 콘텐츠 속성을 확장하여 정의한다면, 이에 대한 처리는 각 사용자 에이전트의 책임입니다. 현재 이벤트 핸들러 콘텐츠 속성 리스트를 사용할 경우, 안전 기준 설정은 실질적으로 다음과 같이 동작합니다:

{
  "removeElements": [
    {
      "namespace": "http://www.w3.org/1999/xhtml",
      "name": "script"
    },
    {
      "namespace": "http://www.w3.org/1999/xhtml",
      "name": "frame"
    },
    {
      "namespace": "http://www.w3.org/1999/xhtml",
      "name": "iframe"
    },
    {
      "namespace": "http://www.w3.org/1999/xhtml",
      "name": "object"
    },
    {
      "namespace": "http://www.w3.org/1999/xhtml",
      "name": "embed"
    },
    {
      "namespace": "http://www.w3.org/2000/svg",
      "name": "script"
    },
    {
      "namespace": "http://www.w3.org/2000/svg",
      "name": "use"
    }
  ],
  "removeAttributes": [
    "onafterprint",
    "onauxclick",
    "onbeforeinput",
    "onbeforematch",
    "onbeforeprint",
    "onbeforeunload",
    "onbeforetoggle",
    "onblur",
    "oncancel",
    "oncanplay",
    "oncanplaythrough",
    "onchange",
    "onclick",
    "onclose",
    "oncontextlost",
    "oncontextmenu",
    "oncontextrestored",
    "oncopy",
    "oncuechange",
    "oncut",
    "ondblclick",
    "ondrag",
    "ondragend",
    "ondragenter",
    "ondragleave",
    "ondragover",
    "ondragstart",
    "ondrop",
    "ondurationchange",
    "onemptied",
    "onended",
    "onerror",
    "onfocus",
    "onformdata",
    "onhashchange",
    "oninput",
    "oninvalid",
    "onkeydown",
    "onkeypress",
    "onkeyup",
    "onlanguagechange",
    "onload",
    "onloadeddata",
    "onloadedmetadata",
    "onloadstart",
    "onmessage",
    "onmessageerror",
    "onmousedown",
    "onmouseenter",
    "onmouseleave",
    "onmousemove",
    "onmouseout",
    "onmouseover",
    "onmouseup",
    "onoffline",
    "ononline",
    "onpagehide",
    "onpagereveal",
    "onpageshow",
    "onpageswap",
    "onpaste",
    "onpause",
    "onplay",
    "onplaying",
    "onpopstate",
    "onprogress",
    "onratechange",
    "onreset",
    "onresize",
    "onrejectionhandled",
    "onscroll",
    "onscrollend",
    "onsecuritypolicyviolation",
    "onseeked",
    "onseeking",
    "onselect",
    "onslotchange",
    "onstalled",
    "onstorage",
    "onsubmit",
    "onsuspend",
    "ontimeupdate",
    "ontoggle",
    "onunhandledrejection",
    "onunload",
    "onvolumechange",
    "onwaiting",
    "onwheel"
  ]
}
"javascript:" 네비게이션이 "안전하지 않음"으로 간주되는 내장 네비게이팅 URL 속성 리스트는 다음과 같습니다:

«[
[ { "name" → "a", "namespace" → HTML namespace }, { "name" → "href", "namespace" → null } ],
[ { "name" → "area", "namespace" → HTML namespace }, { "name" → "href", "namespace" → null } ],
[ { "name" → "base", "namespace" → HTML namespace }, { "name" → "href", "namespace" → null } ],
[ { "name" → "button", "namespace" → HTML namespace }, { "name" → "formaction", "namespace" → null } ],
[ { "name" → "form", "namespace" → HTML namespace }, { "name" → "action", "namespace" → null } ],
[ { "name" → "iframe", "namespace" → HTML namespace }, { "name" → "src", "namespace" → null } ],
[ { "name" → "input", "namespace" → HTML namespace }, { "name" → "formaction", "namespace" → null } ],
[ { "name" → "a", "namespace" → SVG namespace }, { "name" → "href", "namespace" → null } ],
[ { "name" → "a", "namespace" → SVG namespace }, { "name" → "href", "namespace" → XLink namespace } ],

내장 애니메이팅 URL 속성 리스트[SVG11]에서 선언적으로 네비게이션 요소를 "javascript:" URL로 변경할 때 사용할 수 있으며, 다음과 같습니다:

«[
[ { "name" → "animate", "namespace" → SVG namespace }, { "name" → "attributeName", "namespace" → null] } ],
[ { "name" → "animateMotion", "namespace" → SVG namespace }, { "name" → "attributeName", "namespace" → null } ],
[ { "name" → "animateTransform", "namespace" → SVG namespace }, { "name" → "attributeName", "namespace" → null } ],
[ { "name" → "set", "namespace" → SVG namespace }, { "name" → "attributeName", "namespace" → null } ],

4. 보안 고려사항

Sanitizer API는 DOM 기반 크로스사이트 스크립팅(XSS)을 방지하기 위해, 제공된 HTML 콘텐츠를 순회하며 설정에 따라 요소 및 속성을 제거합니다. 명세된 API는 스크립트 실행이 가능한 마크업이 남는 Sanitizer 객체의 생성을 지원해서는 안 되며, 그렇게 된다면 위협 모델의 버그입니다.

그렇다고 해도, Sanitizer API의 올바른 사용만으로는 보호할 수 없는 보안 이슈들이 있으며, 해당 시나리오들은 다음 절에서 설명합니다.

4.1. 서버 측 반사 및 저장 XSS

이 절은 규범적인 내용이 아닙니다.

Sanitizer API는 DOM 내에서만 동작하며, 기존 DocumentFragment를 순회하고 필터링하는 기능을 추가합니다. Sanitizer는 서버 측 반사 또는 저장 XSS를 다루지 않습니다.

4.2. DOM 클로버링

이 절은 규범적인 내용이 아닙니다.

DOM 클로버링은 악의적인 HTML이 id 또는 name 속성으로 요소를 명명하여, 응용 프로그램이 DOM의 HTML 요소의 children과 같은 프로퍼티가 악성 콘텐츠에 의해 가려지는 공격을 의미합니다.

Sanitizer API는 기본 상태에서는 DOM 클로버링 공격을 방어하지 않지만, idname 속성을 제거하도록 설정할 수 있습니다.

4.3. 스크립트 개짓을 이용한 XSS

이 절은 규범적인 내용이 아닙니다.

스크립트 개짓(script gadget)은 공격자가 인기 있는 자바스크립트 라이브러리의 기존 애플리케이션 코드를 활용해 자신의 코드를 실행시키는 기법입니다. 이는 무해해 보이는 코드나 실제로는 해롭지 않은 DOM 노드를 삽입하여, 프레임워크가 해당 입력을 해석해 자바스크립트를 실행하도록 하는 방식입니다.

Sanitizer API는 이러한 공격을 방지할 수 없으며, 페이지 작성자가 일반적으로 알 수 없는 요소를 명시적으로 허용해야 하며, 추가적으로 알 수 없는 속성, 요소 및 data-, slot 속성, <slot>, <template>와 같이 템플릿 및 프레임워크 특화 코드를 명시적으로 설정해야 합니다. 이러한 제한이 충분하지 않을 수 있으므로, 페이지 작성자에게 서드파티 라이브러리의 동작을 주의 깊게 살필 것을 권장합니다.

4.4. 변형된 XSS(mXSS)

이 절은 규범적인 내용이 아닙니다.

변형된 XSS(mXSS)는 올바른 문맥 없이 HTML 조각을 파싱할 때 파서 문맥 불일치에 기반한 공격을 의미합니다. 특히, 파싱된 HTML 프래그먼트를 문자열로 직렬화한 뒤, 해당 문자열을 다른 부모 요소에 삽입할 때 파싱 및 해석 결과가 동일하다는 보장이 없습니다. 이런 공격의 예로는 외부 콘텐츠나 잘못 중첩된 태그의 파싱 동작 변화를 이용하는 방법이 있습니다.

Sanitizer API는 문자열을 노드 트리로 변환하는 기능만 제공합니다. 모든 sanitizer 함수는 암묵적으로 문맥을 전달합니다: Element.setHTML()은 현재 요소를 사용하고, Document.parseHTML()은 새 문서를 생성합니다. 따라서 Sanitizer API는 직접적으로 변형된 XSS의 영향을 받지 않습니다.

개발자가 정화된 노드 트리를 .innerHTML 등으로 문자열로 받아 다시 파싱한다면, 변형된 XSS가 발생할 수 있습니다. 이런 관행은 권장하지 않습니다. HTML을 문자열로 처리하거나 전달해야 한다면 해당 문자열은 신뢰하지 않아야 하며, DOM에 삽입할 때 반드시 다시 정화해야 합니다. 즉, 정화 후 직렬화된 HTML 트리는 더 이상 정화된 것으로 간주할 수 없습니다.

mXSS에 대한 더 자세한 설명은 [MXSS]를 참고하세요.

5. 감사의 말씀

이 작업은 cure53의 [DOMPURIFY], Internet Explorer의 window.toStaticHTML() 및 Ben Bucksch의 원래 [HTMLSanitizer]에서 영감을 받았습니다. Anne van Kesteren, Krzysztof Kotowicz, Tom Schuster, Luke Warlow, Guillaume Weghsteen, Mike West에게 소중한 피드백을 주셔서 감사합니다.

색인

이 명세서에서 정의된 용어

참고 명세에서 정의된 용어

참고 문헌

규범 참고 문헌

[DOM]
Anne van Kesteren. DOM 표준. 현행 표준. URL: https://dom.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML 표준. 현행 표준. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 표준. 현행 표준. URL: https://infra.spec.whatwg.org/
[TRUSTED-TYPES]
Krzysztof Kotowicz. Trusted Types. URL: https://w3c.github.io/trusted-types/dist/spec/
[URL]
Anne van Kesteren. URL 표준. 현행 표준. URL: https://url.spec.whatwg.org/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 표준. 현행 표준. URL: https://webidl.spec.whatwg.org/

참고용 참고 문헌

[DOMPURIFY]
DOMPurify. URL: https://github.com/cure53/DOMPurify
[HTMLSanitizer]
HTML Sanitizer. URL: https://www.bucksch.org/1/projects/mozilla/108153/
[MathML]
Patrick D F Ion; Robert R Miner. 수학 표기 언어 (MathML™) 1.01 명세. 2023년 3월 7일. REC. URL: https://www.w3.org/TR/REC-MathML/
[MXSS]
mXSS 공격: innerHTML 변형을 이용한 잘 보안된 웹 애플리케이션 공격. URL: https://cure53.de/fp170.pdf
[SafeMathML]
MathML 안전 목록. URL: https://w3c.github.io/mathml-docs/mathml-safe-list
[SVG11]
Erik Dahlström; et al. 스케일러블 벡터 그래픽스 (SVG) 1.1 (두번째 판). 2011년 8월 16일. REC. URL: https://www.w3.org/TR/SVG11/

IDL 색인

enum SanitizerPresets { "default" };
dictionary SetHTMLOptions {
  (Sanitizer or SanitizerConfig or SanitizerPresets) sanitizer = "default";
};
dictionary SetHTMLUnsafeOptions {
  (Sanitizer or SanitizerConfig or SanitizerPresets) sanitizer = {};
};

[Exposed=Window]
interface Sanitizer {
  constructor(optional (SanitizerConfig or SanitizerPresets) configuration = "default");

  // Query configuration:
  SanitizerConfig get();

  // Modify a Sanitizer’s lists and fields:
  undefined allowElement(SanitizerElementWithAttributes element);
  undefined removeElement(SanitizerElement element);
  undefined replaceElementWithChildren(SanitizerElement element);
  undefined allowAttribute(SanitizerAttribute attribute);
  undefined removeAttribute(SanitizerAttribute attribute);
  undefined setComments(boolean allow);
  undefined setDataAttributes(boolean allow);

  // Remove markup that executes script. May modify multiple lists:
  undefined removeUnsafe();
};

dictionary SanitizerElementNamespace {
  required DOMString name;
  DOMString? _namespace = "http://www.w3.org/1999/xhtml";
};

// Used by "elements"
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;
};

이슈 색인

요소별 속성 리스트 및 문법 오류에 대한 에러 체크가 아직 부족합니다.