Back

HTML Sanitizer API 初探

HTML Sanitizer API 初探

如果你曾经使用 innerHTML 来渲染用户生成的内容,那么你就已经接受了一定程度的 XSS 风险。通常的解决方案是引入像 DOMPurify 这样的库来先对字符串进行清理。这种方法有效,但意味着需要额外传输 JavaScript 代码,并且需要信任第三方库能够持续跟进浏览器不断演进的解析器行为。HTML Sanitizer API 是浏览器对这一问题的解决方案——即使它还没有在所有环境中做好生产使用的准备,现在也值得了解。

核心要点

  • innerHTML 在解析和注入标记时不进行任何安全检查,这使其在处理用户生成内容时成为持续存在的 XSS 攻击向量。
  • HTML Sanitizer API 引入了像 Element.setHTML() 这样的安全方法,在将内容插入 DOM 之前会自动剥离脚本、事件处理器和危险的 URL。
  • 你可以通过允许列表或移除列表来配置 Sanitizer,精确控制哪些元素和属性能够在清理过程中保留下来。
  • 截至 2026 年初,浏览器支持仍然有限,因此生产代码应该使用特性检测并回退到 DOMPurify。

为什么 innerHTML 一直存在风险

innerHTML 会完全按照你的要求执行:解析字符串并将结果注入到 DOM 中,不会提出任何质疑。这很方便,直到有人将 <img src=x onerror="stealCookies()"> 作为评论或用户名传入。

像 DOMPurify 这样的库通过自己解析字符串、遍历生成的 DOM 树并剥离任何危险内容,然后再将其返回给你来解决这个问题。这种方法有效,但很脆弱。浏览器的解析行为会随时间变化,而运行在用户空间的库必须不断追赶这些变化。相比之下,浏览器本身始终清楚地知道它将如何解析和执行给定的标记片段。

HTML Sanitizer API 提供了什么

原生 HTML Sanitizer API 将清理功能移入了浏览器。你不再直接设置 innerHTML,而是使用新方法来一步完成解析、清理和注入。

安全方法是你最常使用的:

  • Element.setHTML()
  • ShadowRoot.setHTML()
  • Document.parseHTML()

这些方法始终会剥离 XSS 不安全的内容——<script> 标签、像 onclick 这样的事件处理器属性、导航属性中的 javascript: URL——无论你传入什么配置。在没有任何配置的情况下,setHTML() 本质上是 innerHTML 的即插即用替代品,并带有自动 XSS 防护:

const userContent = `<p>Hello!</p><script>alert('xss')<\/script>`;
document.getElementById("output").setHTML(userContent);
// 渲染结果: <p>Hello!</p>
// <script> 被静默丢弃

不安全方法——setHTMLUnsafe()ShadowRoot.setHTMLUnsafe()Document.parseHTMLUnsafe()——给你更多控制权。它们只应用你提供的清理器配置,而不强制执行 XSS 安全基线。只有在你有特定理由需要允许安全方法会阻止的元素或属性时才使用这些方法,并且只能使用精心构建的配置。

配置 Sanitizer

两类方法都接受可选的 Sanitizer 实例或配置字典。你可以构建允许配置(精确指定允许的内容,丢弃其他所有内容)或移除配置(指定要剥离的内容,允许其他所有内容)。

允许配置通常是更安全的选择:

const sanitizer = new Sanitizer({
  elements: ["p", "b", "em", "a"],
  attributes: ["href"]
});

document.getElementById("comments").setHTML(untrustedInput, { sanitizer });

Sanitizer 类还暴露了像 allowElement()removeElement() 这样的方法,用于以编程方式修改配置,同时保持其内部一致性。

浏览器支持及应对方案

这是实际情况:截至 2026 年初,HTML Sanitizer API 才刚刚开始在浏览器中发布。Firefox 148 添加了支持,Chrome 146 紧随其后,但广泛的跨浏览器可用性仍在追赶中。它还不是 Baseline Web 平台的一部分,这意味着你现在还不能依赖它对所有用户可用。你可以在 Can I Use 上关注当前的支持情况。

对于生产代码,使用特性检测并回退到 DOMPurify:

if (typeof Element.prototype.setHTML === "function") {
  element.setHTML(untrustedHTML);
} else {
  element.innerHTML = DOMPurify.sanitize(untrustedHTML);
}

结论

HTML Sanitizer API 代表了浏览器标准应该做的事情:将一个危险的、广泛被误用的模式转变为安全且简单的路径。setHTML()innerHTML 的对比还不是真正的争论——浏览器支持已经为你做出了决定。但现在理解这个 API 意味着你将准备好在支持范围扩大时采用它,并且你将更清楚地了解浏览器原生 HTML 清理实际上是为了做什么而设计的。

常见问题

不能可靠地使用。截至 2026 年初,Firefox 和 Chrome 已经发布了支持,但该 API 仍然是一个可用性有限的特性,不是 Baseline Web 平台的一部分。对于生产应用,使用特性检测来检查 Element.prototype.setHTML 是否存在,并在 API 不可用时回退到像 DOMPurify 这样的库。这让你在支持的地方使用原生清理,同时在其他地方保持安全性。

setHTML 始终强制执行基线 XSS 安全策略。无论你的配置如何,它都会剥离脚本标签、事件处理器属性和危险的 URL。setHTMLUnsafe 只应用你提供的清理器配置,没有这个安全网。只有在你明确需要允许安全方法会阻止的元素或属性时才使用 setHTMLUnsafe。

还不能。DOMPurify 仍然需要作为缺乏 Sanitizer API 支持的浏览器的回退方案。即使浏览器覆盖率达到全面,DOMPurify 提供的更细粒度的配置选项可能是某些项目所需要的。但随着时间推移,原生 API 应该能够处理大多数清理用例,而无需第三方依赖。

允许配置精确指定允许哪些元素和属性,并丢弃其他所有内容。移除配置指定要剥离的内容,并允许未明确列出的所有内容。允许配置通常更安全,因为它默认阻止未知或新元素,而不是意外地允许它们。

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay