ShadowRoot: setHTML() 方法

可用性有限

此特性不是基线特性,因为它在一些最广泛使用的浏览器中不起作用。

ShadowRoot 接口的 setHTML() 方法提供了一种 XSS 安全的方法,可以将 HTML 字符串解析和清理为 DocumentFragment,然后替换 Shadow DOM 中现有的树。

语法

js
setHTML(input)
setHTML(input, options)

参数

input

定义要清理和注入到 shadow root 中的 HTML 的字符串。

options 可选

一个包含以下可选参数的 options 对象

sanitizer

一个 SanitizerSanitizerConfig 对象,它定义了输入中允许或移除哪些元素,或者字符串 "default" 用于默认的 sanitizer 配置。请注意,如果配置需要重用,通常 SanitizerSanitizerConfig 更高效。如果未指定,则使用默认的 sanitizer 配置。

返回值

无 (undefined)。

异常

TypeError

如果 options.sanitizer 传递了以下内容,则抛出此错误

描述

setHTML() 方法提供了一种 XSS 安全的方法,可以解析和清理 HTML 字符串,并使用它来替换 Shadow DOM 中现有的树。

setHTML() 会移除 sanitizer 配置不允许的任何 HTML 实体,并进一步移除任何 XSS 不安全的元素或属性——无论它们是否被 sanitizer 配置允许。

如果在 options.sanitizer 参数中未指定 sanitizer 配置,则 setHTML() 将使用默认的 Sanitizer 配置。此配置允许所有被认为是 XSS 安全的元素和属性,从而禁止被认为不安全的实体。可以指定自定义 sanitizer 或 sanitizer 配置来选择允许或移除哪些元素、属性和注释。请注意,即使 sanitizer 配置允许不安全选项,在使用此方法时(它隐式调用 Sanitizer.removeUnsafe()),它们仍会被移除。

在将不受信任的 HTML 字符串插入 shadow DOM 时,应使用 setHTML() 代替 ShadowRoot.innerHTML。还应使用它代替 ShadowRoot.setHTMLUnsafe(),除非有特定需求需要允许不安全的元素和属性。

请注意,由于此方法始终对 XSS 不安全实体的输入字符串进行清理,因此它不受 Trusted Types API 的保护或验证。

示例

基本用法

此示例显示了您可以使用 setHTML() 来清理和注入 HTML 字符串的一些方法。

首先,我们将创建我们想要目标化的 ShadowRoot。这可以通过使用 Element.attachShadow() 以编程方式创建,但在此示例中,我们将以声明方式创建 root。

html
<div id="host">
  <template shadowrootmode="open">
    <span>A span element in the shadow DOM</span>
  </template>
</div>

我们可以像这样从 #host 元素获取 shadow root 的句柄

js
const shadow = document.querySelector("#host").shadowRoot;

以下代码显示了如何使用字符串和不同的 sanitizers 调用 setHTML() 来过滤并将 HTML 注入 shadow root。

js
// Define unsanitized string of HTML
const unsanitizedString = "abc <script>alert(1)<" + "/script> def";

// setHTML() with default sanitizer
shadow.setHTML(unsanitizedString);

// Define custom Sanitizer and use in setHTML()
// This allows only elements: <div>, <p>, <span> (<script> is unsafe and will be removed)
const sanitizer1 = new Sanitizer({ elements: ["div", "p", "span", "script"] });
shadow.setHTML(unsanitizedString, { sanitizer: sanitizer1 });

// Define custom SanitizerConfig within setHTML()
// This removes elements <div>, <p>, <span>, <script>, and any other unsafe elements/attributes
shadow.setHTML(unsanitizedString, {
  sanitizer: { removeElements: ["div", "p", "span", "script"] },
});

setHTML() 实时示例

此示例提供了当使用不同 sanitizers 调用该方法时的“实时”演示。代码定义了您可以单击的按钮,分别使用默认 sanitizer 和自定义 sanitizer 来清理和注入 HTML 字符串。原始字符串和清理后的 HTML 会被记录下来,以便您在每种情况下都可以检查结果。

HTML

HTML 定义了两个 <button> 元素用于应用不同的 sanitizers,另一个按钮用于重置示例,以及一个包含声明式 shadow root 的 <div>

html
<button id="buttonDefault" type="button">Default</button>
<button id="buttonAllowScript" type="button">allowScript</button>
<button id="reload" type="button">Reload</button>

<div id="host">
  <template shadowrootmode="open">
    <span>I am in the shadow DOM </span>
  </template>
</div>

JavaScript

首先,我们为重置按钮定义处理程序。

js
const reload = document.querySelector("#reload");
reload.addEventListener("click", () => document.location.reload());

然后,我们定义要清理的字符串,该字符串将对所有情况都相同。它包含 <script> 元素和 onclick 处理程序,两者都被认为是 XSS 不安全的。我们还获取变量 shadow,它是我们对 shadow root 的句柄。

js
// Define unsafe string of HTML
const unsanitizedString = `
  <div>
    <p>Paragraph to inject into shadow DOM. <button onclick="alert('You clicked the button!')">Click me</button></p>
    <script src="path/to/a/module.js" type="module"><script>
  </div>
`;

const shadow = document.querySelector("#host").shadowRoot;

接下来,我们定义将 shadow root 设置为默认 sanitizer 的按钮的点击处理程序。这应该在插入 HTML 字符串之前剥离所有不安全的实体。请注意,您可以在 Sanitizer() 构造函数示例中确切地看到哪些元素被移除。

js
const defaultSanitizerButton = document.querySelector("#buttonDefault");
defaultSanitizerButton.addEventListener("click", () => {
  // Set the content of the element using the default sanitizer
  shadow.setHTML(unsanitizedString);

  // Log HTML before sanitization and after being injected
  logElement.textContent =
    "Default sanitizer: remove &lt;script&gt; element and onclick attribute\n\n";
  log(`\nunsanitized: ${unsanitizedString}`);
  log(`\nsanitized: ${shadow.innerHTML}`);
});

下一个点击处理程序使用一个自定义 sanitizer 来设置目标 HTML,该 sanitizer 只允许 <div><p><script> 元素。请注意,因为我们使用的是 setHTML 方法,所以 <script> 也会被移除!

js
const allowScriptButton = document.querySelector("#buttonAllowScript");
allowScriptButton.addEventListener("click", () => {
  // Set the content of the element using a custom sanitizer
  const sanitizer1 = new Sanitizer({
    elements: ["div", "p", "script"],
  });
  shadow.setHTML(unsanitizedString, { sanitizer: sanitizer1 });

  // Log HTML before sanitization and after being injected
  logElement.textContent =
    "Sanitizer: {elements: ['div', 'p', 'script']}\n Script removed even though allowed\n";
  log(`\nunsanitized: ${unsanitizedString}`);
  log(`\nsanitized: ${shadow.innerHTML}`);
});

结果

单击“Default”和“allowScript”按钮,分别查看默认 sanitizer 和自定义 sanitizer 的效果。请注意,因为我们使用的是相同的 sanitization 方法,所以在这两种情况下,<script> 元素和 onclick 处理程序都会被移除,即使它们被 sanitizer 显式允许。

规范

规范
HTML Sanitizer API
# dom-shadowroot-sethtml

浏览器兼容性

另见