HTML 清理器 API

社区组草案报告,

当前版本:
https://wicg.github.io/sanitizer-api/
问题跟踪:
GitHub
规范内联
编辑者:
Frederik Braun (Mozilla)
Mario Heiderich (Cure53)
Daniel Vogelheim (Google LLC)

摘要

本文档指定了一组 API,允许开发者处理不受信任的 HTML 输入并对其进行清理,以便安全地插入到文档的 DOM 中。

本文档的状态

本规范由 Web 平台孵化器社区组 发布。 它不是 W3C 标准,也不在 W3C 标准轨道上。 请注意,根据 W3C 社区贡献者许可协议 (CLA),存在有限的选择退出权,并适用其他条件。 了解更多关于 W3C 社区和业务组 的信息。

1. 介绍

本节为非规范性内容。

Web 应用程序通常需要在客户端处理 HTML 字符串,例如作为客户端模板解决方案的一部分,或者用于渲染用户生成的内容等。然而,以安全的方式处理这些字符串是困难的。将字符串拼接在一起并直接插入到 ElementinnerHTML 中的简单方法存在风险,因为它可能以多种意想不到的方式导致 JavaScript 执行。

[DOMPURIFY] 这样的库试图通过在插入之前仔细解析和清理字符串来解决这个问题,它们通过构建 DOM 并使用允许列表过滤其成员。然而,这种方法被证明是脆弱的,因为暴露给 Web 的解析 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. compliantHTML 为调用 Get Trusted Type compliant string 算法的结果,参数为 TrustedHTMLthisrelevant global objecthtml、“Element setHTMLUnsafe” 和 “script”。

  2. 如果 this 是一个 template 元素,则令 targetthistemplate contents;否则为 this

  3. 给定 targetthiscompliantHTMLoptions 和 false,设置并过滤 HTML

ElementsetHTML(html, options) 方法步骤如下:
  1. 如果 this 是一个 template,则令 targetthistemplate contents; 否则为 this

  2. 给定 targetthishtmloptions 和 true,设置并过滤 HTML

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. compliantHTML 为调用 Get Trusted Type compliant string 算法的结果,参数为 TrustedHTML, thisrelevant global object, html, "ShadowRoot setHTMLUnsafe", 和 "script".

  2. 使用 thisthisshadow host (作为上下文元素)、 compliantHTMLoptions 和 false,设置并过滤 HTML

ShadowRootsetHTML(html, options) 方法步骤如下:
  1. 使用 this (作为目标)、this (作为上下文元素)、 htmloptions 和 true,设置并过滤 HTML

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. compliantHTML 为调用 Get Trusted Type compliant string 算法的结果,参数为 TrustedHTMLthisrelevant global objecthtml、“Document parseHTMLUnsafe” 和 “script”。

  2. document 为一个新的 Document, 其 content type 为 "text/html"。

    注意:由于 document 没有浏览上下文,因此脚本被禁用。

  3. documentallow declarative shadow roots 设置为 true。

  4. 给定 documentcompliantHTML从字符串解析 HTML

  5. sanitizer 为使用 options 和 false 调用 从选项获取清理器实例的结果。

  6. 使用 sanitizer 和 false 在 document 上调用 sanitize

  7. 返回 document

parseHTML(html, options) 方法步骤如下:
  1. document 为一个新的 Document, 其 content type 为 "text/html"。

    注意:由于 document 没有浏览上下文,因此脚本被禁用。

  2. documentallow declarative shadow roots 设置为 true。

  3. 给定 documenthtml从字符串解析 HTML

  4. sanitizer 为使用 options 和 true 调用 从选项获取清理器实例的结果。

  5. 使用 sanitizer 和 true 在 document 上调用 sanitize

  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();
};

一个 Sanitizer 有一个关联的 配置,它是一个 SanitizerConfig

构造函数(configuration) 方法的步骤如下:
  1. 如果 configuration 是一个 SanitizerPresets 字符串,那么:

    1. 断言configuration default

    2. configuration 设置为内置的安全默认配置

  2. valid 为在 this 上使用 configuration 和 true 调用设置配置的返回值。

  3. 如果 valid 为 false,则抛出一个 TypeError

get() 方法的步骤是返回 this配置的值。
allowElement(element) 方法的步骤是使用 elementthis配置允许一个元素
removeElement(element) 方法的步骤是使用 elementthis配置移除一个元素
replaceElementWithChildren(element) 方法的步骤是使用 elementthis配置用其子元素替换一个元素
allowAttribute(attribute) 方法的步骤是使用 attributethis配置允许一个属性
removeAttribute(attribute) 方法的步骤是使用 attributethis配置移除一个属性
setComments(allow) 方法的步骤是使用 allowthis配置设置注释
setDataAttributes(allow) 方法的步骤是使用 allowthis配置设置数据属性
removeUnsafe() 方法的步骤是使用在 this配置上调用移除不安全内容的结果来更新 this配置

2.3. 配置字典

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;
};

3. 算法

设置和过滤 HTML,给定一个 ElementDocumentFragment target,一个 Element contextElement,一个字符串 html,一个字典 options,以及一个布尔值 safe
  1. 如果 safe 为真且 contextElement本地名称是 "script" 且 contextElement命名空间HTML 命名空间SVG 命名空间,则返回。

  2. sanitizer 为使用 optionssafe 调用从选项获取 Sanitizer 实例的结果。

  3. newChildren 为给定 contextElementhtml 和 true 的HTML 片段解析算法的结果。

  4. fragment 为一个新的 DocumentFragment, 其节点文档contextElement节点文档

  5. 对于 newChildren 中的每个 node,将 node 追加fragment

  6. 使用 sanitizersafefragment 上运行净化

  7. target 内用 fragment 替换所有内容

要从一个字典 options 和一个布尔值 safe 从选项获取 Sanitizer 实例, 执行:

注意:此算法同时适用于 SetHTMLOptionsSetHTMLUnsafeOptions。 它们仅在默认值上有所不同。

  1. sanitizerSpec 为 “default”。

  2. 如果 options["sanitizer"] 存在,则:

    1. sanitizerSpec 设置为 options["sanitizer"]

  3. 断言sanitizerSpec 是一个 Sanitizer 实例、一个作为 SanitizerPresets 成员的字符串,或一个字典

  4. 如果 sanitizerSpec 是一个字符串

    1. 断言sanitizerSpec default

    2. sanitizerSpec 设置为内置安全默认配置

  5. 断言sanitizerSpec 是一个 Sanitizer 实例,或一个字典

  6. 如果 sanitizerSpec 是一个字典

    1. sanitizer 为一个新的 Sanitizer 实例。

    2. setConfigurationResult 为在 sanitizer 上使用 sanitizerSpec safe 调用设置配置的结果。

    3. 如果 setConfigurationResult 为 false,抛出一个 TypeError

    4. sanitizerSpec 设置为 sanitizer

  7. 断言sanitizerSpec 是一个 Sanitizer 实例。

  8. 返回 sanitizerSpec

3.1. 净化算法

对于主要的 净化 操作,使用一个 ParentNode node,一个 Sanitizer sanitizer,以及一个 布尔值 safe,运行以下步骤:
  1. configurationsanitizer配置 的值。

  2. 如果 safe 为 true,则将 configuration 设置为在 configuration 上调用 移除不安全内容 的结果。

  3. nodeconfiguration 上调用 核心净化,并将 handleJavascriptNavigationUrls 设置为 safe

核心净化 操作,使用一个 ParentNode node,一个 SanitizerConfig configuration,以及一个 布尔值 handleJavascriptNavigationUrls,从 node 开始遍历 DOM 树,并可能递归处理一些特殊情况(例如模板内容)。它包含以下步骤:
  1. 对于 node子节点 中的每个 child

    1. 断言child 实现TextCommentElement, 或 DocumentType

      注意:目前,此算法仅在 HTML 解析器的输出上调用,该断言应成立。DocumentType 应仅出现在 parseHTMLparseHTMLUnsafe 中。 如果将来此算法将用于不同的上下文中,则需要重新检查此假设。

    2. 如果 child 实现DocumentType, 则 继续

    3. 如果 child 实现Text, 则 继续

    4. 如果 child 实现Comment

      1. 如果 configuration["comments"] 不为 true,则 移除 child

    5. 否则:

      1. elementName 为一个 SanitizerElementNamespace, 其包含 child本地名称命名空间

      2. 如果 configuration["replaceWithChildrenElements"] 包含 elementName

        1. child 上使用 configurationhandleJavascriptNavigationUrls 调用 核心净化

        2. child 内部用 child子节点 替换所有内容

        3. 继续

      3. 如果 configuration["removeElements"] 包含 elementName, 或者如果 configuration["elements"] 不 为空 且不 包含 elementName

        1. 移除 child

        2. 继续

      4. 如果 elementName 等于 «[ "name" → "template", "namespace" → HTML 命名空间

        1. 则在 child模板内容 上使用 configurationhandleJavascriptNavigationUrls 调用 核心净化

      5. 如果 child 是一个 影子宿主, 则在 child影子根 上使用 configurationhandleJavascriptNavigationUrls 调用 核心净化

      6. 对于 child属性列表 中的每个 attribute

        1. attrName 为一个 SanitizerAttributeNamespace, 其包含 attribute本地名称命名空间

        2. 如果 configuration["removeAttributes"] 包含 attrName, 则从 child 中移除 attribute

        3. 如果 configuration["elements"]["removeAttributes"] 包含 attrName,则从 child 中移除 attribute

        4. 如果以下所有条件均为 false,则从 child 中移除 attribute

        5. 如果 handleJavascriptNavigationUrls 为真:

          1. 如果 «[elementName, attrName]» 匹配 内置导航 URL 属性列表 中的一个条目,并且如果 attribute 包含 javascript: URL,则从 child 中移除 attribute

          2. 如果 child命名空间 MathML 命名空间attr本地名称 "href" 且 attr命名空间nullXLink 命名空间attr 包含 javascript: URL, 则 移除 attr

          3. 如果 内置动画 URL 属性列表 包含 «[elementName, attrName]» 且 attr "href" 或 "xlink:href",则 移除 attr

      7. child 上使用 configurationhandleJavascriptNavigationUrls 调用 核心净化

注意:当前浏览器仅在导航时支持 javascript: URL。由于导航本身不是 XSS 威胁,我们处理到 javascript: URL 的导航,但不处理一般的导航。

声明式导航可分为以下几类:

  1. 锚点元素。(HTML 和 SVG 命名空间中的 <a>

  2. 作为表单操作一部分触发导航的表单元素。

  3. [MathML] 允许 任何元素充当锚点

  4. [SVG11] 动画。

前两种情况由 内置导航 URL 属性列表 涵盖。

MathML 的情况由一个单独的规则涵盖,因为本规范中没有形式化的方法来涵盖“每个命名空间的全局”规则。

SVG 动画的情况由 内置动画 URL 属性列表 涵盖。但是由于 SVG 动画元素的解释取决于动画目标,并且在净化期间我们无法知道最终目标是什么,因此 净化 算法会阻止 href 属性的任何动画。

要确定一个 attribute 是否 包含 javascript: URL
  1. url 为在 attribute 上运行 基本 URL 解析器 的结果。

  2. 如果 urlfailure,则返回 false。

  3. 返回 url方案 是否 "javascript"。

3.2. 配置处理

要通过 SanitizerConfig configuration 允许一个元素 element,执行:
  1. element 设置为使用 element 规范化带属性的净化器元素的结果。

  2. configuration["elements"] 中移除 element

  3. element 追加configuration["elements"]。

  4. configuration["removeElements"] 中移除 element

  5. configuration["replaceWithChildrenElements"] 中移除 element

注意:allowElement 的处理比其他方法要复杂一些,因为元素允许列表可以有每个元素的允许和移除属性列表。我们首先从列表中移除给定的元素,然后再添加它,这样做的效果是重置(而不是合并或以其他方式修改)每个元素的列表为传入的内容。换句话说,每个元素的允许和移除列表只能整体设置。

注意:移除操作会匹配名称和命名空间,因此添加带属性的元素仍会从 removeElementsreplaceWithChildrenElements 列表中移除匹配的元素。

要从 SanitizerConfig configuration移除一个元素 element,执行:
  1. element 设置为使用 element 规范化净化器元素的结果。

  2. element 添加configuration["removeElements"]。

  3. configuration["elements"] 列表中移除 element

  4. configuration["replaceWithChildrenElements"] 中移除 element

要从 SanitizerConfig configuration用其子元素替换一个元素 element,执行:
  1. element 设置为使用 element 规范化净化器元素的结果。

  2. element 添加configuration["replaceWithChildrenElements"]。

  3. configuration["removeElements"] 中移除 element

  4. configuration["elements"] 列表中移除 element

要在一个 SanitizerConfig configuration允许一个属性 attribute,执行:
  1. attribute 设置为使用 attribute 规范化净化器属性的结果。

  2. attribute 添加configuration["attributes"]。

  3. configuration["removeAttributes"] 中移除 attribute

要从一个 SanitizerConfig configuration移除一个属性 attribute,执行:
  1. attribute 设置为使用 attribute 规范化净化器属性的结果。

  2. attribute 添加configuration["removeAttributes"]。

  3. configuration["attributes"] 中移除 attribute

要在 SanitizerConfig configuration 上使用 allow 设置注释,执行:
  1. configuration["comments"] 设置为 allow

要在 SanitizerConfig configuration 上使用 allow 设置数据属性,执行:
  1. configuration["dataAttributes"] 设置为 allow

注意:虽然此算法称为移除不安全内容,但我们严格按照本规范的意义使用术语“不安全”,以表示在插入文档时将执行 JavaScript 的内容。换句话说,此方法将消除 XSS 的机会。

要从 configuration移除不安全内容,执行以下操作:

  1. 断言内置安全基线配置具有已设置的 removeElementsremoveAttributes 键,但未设置 elementsreplaceWithChildrenElementsattributes

  2. resultconfiguration 的副本。

  3. 对于内置安全基线配置[removeElements] 中的每个 element

    1. 使用 elementresult 调用移除元素

  4. 对于内置安全基线配置[removeAttributes] 中的每个 attribute

    1. 使用 attributeresult 调用移除属性

  5. 对于事件处理程序内容属性中列出的每个 attribute

    1. 使用 attributeresult 调用移除属性

  6. 返回 result

设置配置,给定一个字典 configuration,一个布尔值 allowCommentsAndDataAttributes,以及一个 Sanitizer sanitizer
  1. 对于 configuration["elements"] 中的每个 element,执行:

    1. 使用 elementsanitizer配置调用允许元素

  2. 对于 configuration["removeElements"] 中的每个 element,执行:

    1. 使用 elementsanitizer配置调用移除元素

  3. 对于 configuration["replaceWithChildrenElements"] 中的每个 element,执行:

    1. 使用 elementsanitizer配置调用用其子元素替换元素

  4. 对于 configuration["attributes"] 中的每个 attribute,执行:

    1. 使用 attributesanitizer配置调用允许属性

  5. 对于 configuration["removeAttributes"] 中的每个 attribute,执行:

    1. 使用 attributesanitizer配置调用移除属性

  6. 如果 configuration["comments"] 存在

    1. 则使用 configuration["comments"] 和 sanitizer配置调用设置注释

    2. 否则使用 allowCommentsAndDataAttributessanitizer配置调用设置注释

  7. 如果 configuration["dataAttributes"] 存在

    1. 则使用 configuration["dataAttributes"] 和 sanitizer配置调用设置数据属性

    2. 否则使用 allowCommentsAndDataAttributessanitizer配置调用设置数据属性

  8. 返回以下所有条件是否都为真:

注意:本规范的先前版本对如何规范化配置有详细的定义。现在这实际上已移至方法定义中。

注意:此操作是根据 Sanitizer 上的操作方法定义的。这些方法会从其他列表中移除匹配的条目。最后一步中的大小相等性检查会捕获到这一点。例如:{ allow: ["div", "div"] } 会创建一个在允许列表中包含一个元素的 Sanitizer。最终测试将返回 false,这将导致调用者抛出异常。

此处仍缺少对每个元素属性列表和语法错误的错误检查。

为了规范化带属性的净化器元素 SanitizerElementWithAttributes element,执行以下操作:
  1. result 为使用 element 规范化净化器元素的结果。

  2. 如果 element 是一个字典

    1. 对于 element["attributes"] 中的每个 attribute

      1. 将使用 attribute 规范化净化器属性的结果添加result["attributes"]。

    2. 对于 element["removeAttributes"] 中的每个 attribute

      1. 将使用 attribute 规范化净化器属性的结果添加result["removeAttributes"]。

  3. 返回 result

为了规范化净化器元素 SanitizerElement element, 返回使用 element 和作为默认命名空间的 HTML 命名空间规范化净化器名称的结果。
为了规范化净化器属性 SanitizerAttribute attribute, 返回使用 attribute 和作为默认命名空间的 null 规范化净化器名称的结果。
为了规范化净化器名称 name,使用默认命名空间 defaultNamespace,运行以下步骤:
  1. 断言name 是一个 DOMString 或一个字典

  2. 如果 name 是一个 DOMString, 则返回 «[ "name" → name, "namespace" → defaultNamespace]»。

  3. 断言name 是一个字典name["name"] 存在

  4. namespacename["namespace"](如果它存在), 否则为 defaultNamespace

  5. 如果 namespace 是空字符串,则将其设置为 null。

  6. 返回 «[
    "name" → name["name"],
    "namespace" → namespace
    ]»。

3.3. 支持算法

对于本规范中使用的规范化元素属性名称 列表,列表成员资格基于匹配 "name" 和 "namespace" 条目:

如果存在一个 listentry,它是一个 有序映射,并且其中 item["name"] 等于 entry["name"] 且 item["namespace"] 等于 entry["namespace"],则 Sanitizer 名称 list 包含一个 item
要从一个作为 有序映射list移除一个 item移除 list 中所有满足 item["name"] 等于 entry["name"] 且 item["namespace"] 等于 entry["namespace"] 的 entry
要将一个 name 添加到一个 list 中,其中 name规范化的,并且 list 是一个有序映射
  1. 如果 list 包含 name,则返回。

  2. name 追加list

有序集的相等性是其成员的相等性,但不考虑顺序:如果 AB超集,并且 BA超集,则有序集 AB相等的
要确定一个布尔值 bool, 如果 bool 为 true,则返回 false,否则返回 true。

3.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": []
}

警告:移除不安全内容算法规定额外移除在 [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"
  ]
}
内置导航 URL 属性列表,其中 "javascript:" 导航被视为“不安全”,如下所示:

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

内置动画 URL 属性列表,可用于在 [SVG11] 中声明性地修改导航元素以使用 "javascript:" URL,如下所示:

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

4. 安全注意事项

Sanitizer API 旨在通过遍历提供的 HTML 内容并根据配置移除元素和属性来防止基于 DOM 的跨站脚本攻击。指定的 API 不得支持构建会留下可执行脚本标记的 Sanitizer 对象,这样做将是威胁模型中的一个错误。

话虽如此,正确使用 Sanitizer API 仍无法防范某些安全问题,以下各节将列出这些场景。

4.1. 服务器端反射型和存储型 XSS

本节不具有规范性。

Sanitizer API 仅在 DOM 中运行,并增加了遍历和过滤现有 DocumentFragment 的功能。Sanitizer 无法解决服务器端反射型或存储型 XSS。

4.2. DOM Clobbering

本节不具有规范性。

DOM Clobbering 描述了一种攻击,其中恶意 HTML 通过使用 idname 属性命名元素来混淆应用程序,从而使 DOM 中 HTML 元素的 children 等属性被恶意内容覆盖。

Sanitizer API 在其默认状态下无法防止 DOM Clobbering 攻击,但可以配置为移除 idname 属性。

4.3. 使用脚本小工具的 XSS

本节不具有规范性。

脚本小工具是一种技术,攻击者利用流行 JavaScript 库中的现有应用程序代码来执行自己的代码。这通常通过注入看起来无害的代码或看似惰性的 DOM 节点来完成,这些节点仅由框架解析和解释,然后框架根据该输入执行 JavaScript。

Sanitizer API 无法阻止这些攻击,但要求页面作者通常明确允许未知元素,并且作者还必须明确配置已知广泛用于模板和特定于框架的代码的未知属性、元素和标记,例如 data-slot 属性以及诸如 <slot><template> 之类的元素。我们认为这些限制并非详尽无遗,并鼓励页面作者检查其第三方库是否存在此行为。

4.4. 突变型 XSS

本节不具有规范性。

突变型 XSS 或 mXSS 描述了一种基于在没有正确上下文的情况下解析 HTML 片段时解析器上下文不匹配的攻击。特别是,当已解析的 HTML 片段已序列化为字符串时,不能保证在插入到不同的父元素时该字符串会被完全相同地解析和解释。执行此类攻击的一个示例是依赖于外部内容或错误嵌套标签的解析行为的更改。

Sanitizer API 仅提供将字符串转换为节点树的函数。所有净化器函数都隐式提供上下文: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 Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. 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 Standard. Living Standard. URL: https://url.spec.whatwg.org/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. 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. Mathematical Markup Language (MathML™) 1.01 Specification. 7 March 2023. REC. URL: https://www.w3.org/TR/REC-MathML/
[MXSS]
mXSS Attacks: Attacking well-secured Web-Applications by using innerHTML Mutations. URL: https://cure53.de/fp170.pdf
[SafeMathML]
MathML Safe List. URL: https://w3c.github.io/mathml-docs/mathml-safe-list
[SVG11]
Erik Dahlström; et al. Scalable Vector Graphics (SVG) 1.1 (Second Edition). 16 August 2011. 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;
};

问题索引

每个元素属性列表和语法错误的错误检查仍然缺失。