# DOMPurify
[](https://www.npmjs.com/package/dompurify) [](https://github.com/cure53/DOMPurify/blob/main/LICENSE) [](https://www.npmjs.com/package/dompurify) [](https://github.com/cure53/DOMPurify/network/dependents)  [](https://cloudback.it)
[](https://www.bestpractices.dev/projects/12162) [](https://github.com/cure53/DOMPurify/actions/workflows/build-and-test.yml) [](https://scorecard.dev/viewer/?uri=github.com/cure53/DOMPurify) [](https://badge.socket.dev/npm/package/dompurify/latest) [](https://security.snyk.io/package/npm/dompurify)
DOMPurify 是一款仅依赖 DOM、速度极快且极其宽容的 XSS 净化器,适用于 HTML、MathML 和 SVG。
它也非常简单易用、易于上手。DOMPurify [始于 2014 年 2 月](https://github.com/cure53/DOMPurify/commit/a630922616927373485e0e787ab19e73e3691b2b),如今已发展到 **v3.4.17** 版本。
DOMPurify 以 JavaScript 运行,适用于所有现代浏览器(Safari(10+)、Opera(15+)、Edge、Firefox 和 Chrome——以及几乎所有使用 Blink、Gecko 或 WebKit 的其他环境)。它在 MSIE 或其他旧版浏览器上不会出错,只是什么都不做。
**请注意,[DOMPurify v2.5.9](https://github.com/cure53/DOMPurify/releases/tag/2.5.9) 是最后一个支持 MSIE 的版本。如需与 MSIE 兼容的重要安全更新,请使用 [2.x 分支](https://github.com/cure53/DOMPurify/tree/2.x)。**
我们的自动化测试在每次推送时都会覆盖当前引擎(Ubuntu、macOS 和 Windows 上的 Chromium、Firefox 和 WebKit)上的 9 种浏览器/操作系统组合,另有一个独立的矩阵会在较旧的引擎快照(大致回溯到 Chromium 110、Firefox 108 和 WebKit 16.4,约三年前)上重新运行整套测试,以便也能捕获过时浏览器上的回归问题。我们还在 [jsdom](https://github.com/jsdom/jsdom) 上使用 Node.js v20、v22、v24、v25 和 v26 运行 DOMPurify。已知较旧的 Node 版本也能正常工作,但嘿……不作保证。
DOMPurify 由在 Web 攻击和 XSS 方面拥有深厚背景的安全人员编写。无需担心。更多详情,请阅读我们的[安全目标与威胁模型](https://github.com/cure53/DOMPurify/wiki/Security-Goals-&-Threat-Model)。请务必阅读。真的。如果你喜欢那些血腥细节,[攻击类别与绕过历史](https://github.com/cure53/DOMPurify/wiki/Attack-Classes-&-Bypass-History)页面记录了 DOMPurify 所防御的解析器变异、命名空间、覆盖(clobbering)和模板技巧。
DOMPurify 项目启发了 [HTML Sanitizer API](https://wicg.github.io/sanitizer-api/#sanitizer) 的创建,该 API 已在[许多浏览器](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Sanitizer_API#browser_compatibility)中发布。同样的能力现正直接在 [WHATWG HTML 规范](https://html.spec.whatwg.org/#html-sanitization)中标准化。
## 目录
- [它做什么?](#what-does-it-do)
- [我如何使用它?](#how-do-i-use-it)
- [有演示吗?](#is-there-a-demo)
- [如果我发现了 _安全_ 漏洞怎么办?](#what-if-i-find-a-security-bug)
- [能给我一些净化示例吗?](#some-purification-samples-please)
- [支持哪些内容?](#what-is-supported)
- [像 Internet Explorer 这样的旧版浏览器怎么办?](#what-about-legacy-browsers-like-internet-explorer)
- [DOMPurify 和 Trusted Types 怎么办?](#what-about-dompurify-and-trusted-types)
- [我可以配置 DOMPurify 吗?](#can-i-configure-dompurify)
- [持久化配置](#persistent-configuration)
- [钩子](#hooks)
- [已移除的配置](#removed-configuration)
- [持续集成](#continuous-integration)
- [安全邮件列表](#security-mailing-list)
- [谁做出了贡献?](#who-contributed)
## 它做什么?
DOMPurify 净化 HTML 并防止 XSS 攻击。你可以向 DOMPurify 传入例如一个充满脏 HTML 的字符串,它会返回一个(除非另有配置)包含干净 HTML 的字符串。DOMPurify 会剥离所有包含危险 HTML 的内容,从而防止 XSS 攻击和其他恶意行为。它还快得惊人。我们利用浏览器提供的技术,将它们转化为 XSS 过滤器。你的浏览器越快,DOMPurify 就越快。
## 我如何使用它?
很简单。只需在你的网站上引入 DOMPurify。
### 使用未压缩版本(提供 source-map)
```html
<script type="text/javascript" src="dist/purify.js"></script>
```
### 使用经过压缩和测试的生产版本(提供 source-map)
```html
<script type="text/javascript" src="dist/purify.min.js"></script>
```
之后,你可以通过执行以下代码来清理字符串:
```js
const clean = DOMPurify.sanitize(dirty);
```
或者,如果你喜欢使用 Angular 或类似框架,也可以试试这个:
```js
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize('<b>hello there</b>');
```
生成的 HTML 可以使用 `innerHTML` 写入 DOM 元素,或使用 `document.write()` 写入 DOM。这完全由你决定。
请注意,默认情况下,我们允许 HTML、SVG **和** MathML。如果你只需要 HTML(这可能是一个非常常见的用例),你也可以轻松地进行相应配置:
```js
const clean = DOMPurify.sanitize(dirty, { USE_PROFILES: { html: true } });
```
### 是否存在潜在隐患?
请注意,如果你_先_对 HTML 进行净化,_然后_再对其进行修改,你很容易**使净化的效果失效**。如果你在净化_之后_将净化后的标记交给另一个库处理,请务必确认该库不会自行对 HTML 进行改动。有关安全使用的配方以及值得三思的标签/属性,请参阅[安全目标与威胁模型](https://github.com/cure53/DOMPurify/wiki/Security-Goals-&-Threat-Model);有关后处理和更改标记上下文为何会破坏净化的原因,请参阅[攻击类别与绕过历史](https://github.com/cure53/DOMPurify/wiki/Attack-Classes-&-Bypass-History)。
### 传入 DOM 节点而不是字符串会怎样?
`DOMPurify.sanitize()` 也接受 DOM 节点(`Element`、`DocumentFragment` 或 `Document`)。自 3.4.14 起,对于并非由 HTML 解析器生成的节点,该路径已得到加固:通过 DOM API 构建或以 XML/XHTML 解析的节点(例如通过 `DOMParser` 配合 `application/xhtml+xml` 和 `importNode()`)可能携带保留大小写的属性名,如 `ONERROR`,或者像 `<style>` 这样的原始文本元素,其文本中包含元素子节点或自身的结束标签。这两种形态对字符串净化器来说都是不可见的,因为 HTML 解析器永远无法构建它们,但它们在重新解析时会突破出来。DOMPurify 现在按其精确的 `Attr` 节点移除属性,并将这些字面文本树视为不安全,因此输入侧混合文档上下文的情况已被覆盖。在_输出_侧不混合上下文仍然是你的责任,请参阅上一段。
### 好的,有道理,我们继续
净化标记之后,你还可以查看 `DOMPurify.removed` 属性,了解哪些元素和属性被剔除了。请**不要**使用该属性做出任何安全关键决策。这只是一个满足好奇心的辅助工具。
### 在服务器上运行 DOMPurify
从技术上讲,DOMPurify 也可以在服务器端配合 Node.js 运行。我们的支持工作力求遵循 [Node.js 发布周期](https://nodejs.org/en/about/previous-releases)。
在服务器上运行 DOMPurify 需要存在 DOM,这大概并不令人意外。通常,[jsdom](https://github.com/jsdom/jsdom) 是首选工具,我们**强烈建议**使用最新版本的 _jsdom_。
为什么?因为已知旧版本的 _jsdom_ 存在缺陷,会导致 XSS,_即使_ DOMPurify 100% 正确执行所有操作。例如,_jsdom v19.0.0_ 中存在**已知攻击向量**,这些在 _jsdom v20.0.0_ 中已修复——因此我们真的建议保持 _jsdom_ 为最新版本。
另请注意,虽然存在 [happy-dom](https://github.com/capricorn86/happy-dom) 之类的工具,但目前**不被视为安全**。目前不建议将 DOMPurify 与 _happy-dom_ 结合使用,这很可能导致 XSS。有关你所选择的服务器端 DOM 为何属于可信计算基的一部分的背景信息,请参阅[攻击类别与绕过历史](https://github.com/cure53/DOMPurify/wiki/Attack-Classes-&-Bypass-History)。
在 _jsdom_ 无法运行的运行时上,例如 Cloudflare Workers 或 Vercel Edge,[purify-edge](https://github.com/anzal1/purify-edge) 是一个第三方项目,它在基于 parse5 的 window 上运行未经修改的 DOMPurify。我们的测试套件可以通过 `npm run test:purify-edge` 对其运行,作为兼容性检查。它不由我们维护,测试通过既不代表认可,也不构成安全保证。
除此之外,你完全可以在服务器上使用 DOMPurify。大概吧。这实际上取决于 _jsdom_ 或你在服务器端使用的任何 DOM。如果你能接受这一点,以下就是让它工作的方法:
```bash
npm install dompurify
npm install jsdom
```
对于 _jsdom_(请使用最新版本),以下代码应该可以解决问题:
```js
const createDOMPurify = require('dompurify');
const { JSDOM } = require('jsdom');
const window = new JSDOM('').window;
const DOMPurify = createDOMPurify(window);
const clean = DOMPurify.sanitize('<b>hello there</b>');
```
或者,如果你更喜欢使用导入,也可以这样:
```js
import { JSDOM } from 'jsdom';
import DOMPurify from 'dompurify';
const window = new JSDOM('').window;
const purify = DOMPurify(window);
const clean = purify.sanitize('<b>hello there</b>');
```
如果你在特定环境中遇到无法正常运行的问题,可以参考出色的 [isomorphic-dompurify](https://github.com/kkomelin/isomorphic-dompurify) 项目,它解决了人们可能遇到的许多问题。
```bash
npm install isomorphic-dompurify
```
```js
import DOMPurify from 'isomorphic-dompurify';
const clean = DOMPurify.sanitize('<s>hello</s>');
```
## 有演示吗?
当然有演示
## 如果我发现了安全漏洞怎么办?
首先,请立即通过[电子邮件](mailto:[email protected])联系我们,以便我们着手修复。[PGP 密钥](https://keyserver.ubuntu.com/pks/lookup?op=vindex&search=0xC26C858090F70ADA)
此外,你很可能符合漏洞赏金的条件 的优秀团队在其服务中使用了 DOMPurify,并将我们的库纳入了他们的漏洞赏金范围。因此,如果你找到了绕过或削弱 DOMPurify 的方法,也请查看他们的网站和[漏洞赏金信息](https://www.fastmail.com/about/bugbounty/)。
## 请给一些净化示例?
净化后的标记是什么样的?嗯,[演示](https://cure53.de/purify)展示了一大堆恶意元素的效果。不过,我们也来展示一些更小的示例吧!
```js
DOMPurify.sanitize(''); // becomes <img src="https://raw.githubusercontent.com/cure53/dompurify/main/x">
DOMPurify.sanitize('<svg><g/onload=alert(2)//<p>'); // becomes <svg><g></g></svg>
DOMPurify.sanitize('<p>abcdef</p>'); // becomes <p>abc</p>
DOMPurify.sanitize('<math><mi//xlink:href="data:x,<script>alert(4)</script>">'); // becomes <math><mi></mi></math>
DOMPurify.sanitize('<TABLE><tr><td>HELLO</tr></TABL>'); // becomes <table><tbody><tr><td>HELLO</td></tr></tbody></table>
DOMPurify.sanitize('<UL><li><A HREF=//google.com>click</UL>'); // becomes <ul><li><a href="//google.com">click</a></li></ul>
```
这些只是冰山一角。关于这些样本所来自的攻击类别的完整分类体系——变异型 XSS、命名空间混淆、DOM clobbering、rawtext 突破等——请参阅 [攻击类别与绕过历史](https://github.com/cure53/DOMPurify/wiki/Attack-Classes-&-Bypass-History)。
## 支持哪些内容?
DOMPurify 目前支持 HTML5、SVG 和 MathML。DOMPurify 默认允许 CSS 和 HTML 自定义数据属性。DOMPurify 还支持 Shadow DOM——并会递归地对 DOM 模板进行净化。这仅涵盖开放的 shadow root:封闭的 shadow root 无法从脚本中读取,因此 DOMPurify 既无法看到也无法净化其中的内容。字符串不受影响,因为 DOMPurify 自带的解析器会忽略声明式 shadow root(`<template shadowrootmode>`),但 `setHTMLUnsafe()` 和 `Document.parseHTMLUnsafe()` 不会:如果你用它们解析不受信任的标记,然后将生成的节点传给 DOMPurify(`IN_PLACE`、`RETURN_DOM`、`RETURN_DOM_FRAGMENT`),该标记中的任何封闭 shadow root 都会以未净化的状态返回。请改为将不受信任的标记作为字符串传给 DOMPurify。DOMPurify 还允许你对 HTML 进行净化,以便与 jQuery `$()` 和 `elm.html()` API 配合使用,且不存在任何已知问题。关于默认允许的确切元素和属性集合,请参阅 [默认 TAG 与 ATTRIBUTE 允许列表与阻止列表](https://github.com/cure53/DOMPurify/wiki/Default-TAGs-ATTRIBUTEs-allow-list-&-blocklist) wiki 页面。
## 像 Internet Explorer 这样的旧版浏览器怎么办?
DOMPurify 完全不做任何处理。它只是原样返回你传入的字符串。DOMPurify 暴露了一个名为 `isSupported` 的属性,它会告诉你它是否能够完成其工作,以便你可以制定自己的备用方案。
## DOMPurify 与 Trusted Types 呢?
在 1.0.9 版本中,DOMPurify 添加了对 [Trusted Types API](https://github.com/w3c/webappsec-trusted-types)([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API))的支持。
在 2.0.0 版本中,添加了一个配置标志来控制 DOMPurify 在这方面的行为。
当在 Trusted Types API 可用的环境中使用 `DOMPurify.sanitize` 且 `RETURN_TRUSTED_TYPE` 设置为 `true` 时,它会尝试返回 `TrustedHTML` 值而不是字符串(`RETURN_DOM` 和 `RETURN_DOM_FRAGMENT` 配置选项的行为不变)。
请注意,为了使用 DOMPurify 在 `trustedTypes` 中创建策略,需要设置 `RETURN_TRUSTED_TYPE: false`,因为 `createHTML` 期望的是普通字符串,而不是 `TrustedHTML`。下面的示例展示了这一点。
```js
window.trustedTypes.createPolicy('default', {
createHTML: (to_escape) =>
DOMPurify.sanitize(to_escape, { RETURN_TRUSTED_TYPE: false }),
});
```
当未提供 `TRUSTED_TYPES_POLICY` 时,DOMPurify 会尝试创建其名为 `dompurify` 的内部 Trusted Types 策略。如果你的页面已经定义了自己的策略,并配合严格的 CSP(例如 `trusted-types my-organization`),且该 CSP 不允许名为 `dompurify` 的策略,那么此尝试会被浏览器阻止,并记录一条 `TrustedTypes policy dompurify could not be created.` 警告以及一条 CSP 违规。
要阻止 DOMPurify 创建其内部回退策略,请传入 `TRUSTED_TYPES_POLICY: null`。当你从自己的策略的 `createHTML` 内部调用 `DOMPurify.sanitize` 时,这是正确的选择,这意味着你无需将 `dompurify` 添加到 CSP 的 `trusted-types` 允许列表中。
```js
window.trustedTypes.createPolicy('my-organization', {
createHTML: (input) =>
DOMPurify.sanitize(input, { TRUSTED_TYPES_POLICY: null }),
});
```
**不要**将你自己的包装策略作为 `TRUSTED_TYPES_POLICY` 传回给 DOMPurify(例如通过 `DOMPurify.setConfig({ TRUSTED_TYPES_POLICY: myPolicy })`),尤其是当该策略的 `createHTML` 已经调用了 `DOMPurify.sanitize` 时。从定义上讲这就是循环调用——净化会调用该策略,而该策略又通过调用 DOMPurify 进行净化——DOMPurify 会抛出一个带有说明的 `TypeError` 以防止无限递归。你自己的策略应当调用 DOMPurify;而不应将 DOMPurify 配置为调用你的策略。
如果你希望将这种 `default` 策略模式自动应用到整个页面——使每个 HTML 接收点都被净化,包括遗留代码、第三方组件,以及成千上万你难以找到或重写的 `innerHTML` 赋值——请查看 [DOMFortify](https://github.com/cure53/DOMFortify)。它安装的正是这样一个由 DOMPurify 支持的 Trusted Types `default` 策略,并直接拒绝脚本接收点(`eval`、`script.src` 等)。这是一个刻意独立的项目:DOMPurify 保持为一个专注的净化器,而 DOMFortify 负责处理文档范围的强制执行层,这有意超出了 DOMPurify 的范围。
## 我可以配置 DOMPurify 吗?
可以。内置的默认配置值已经相当不错——但你当然可以覆盖它们。查看 [`/demos`](https://github.com/cure53/DOMPurify/tree/main/demos) 文件夹,可以看到大量关于如何[自定义 DOMPurify](https://github.com/cure53/DOMPurify/tree/main/demos#what-is-this) 的示例。
在你扩大允许列表(`ADD_TAGS`、`ADD_ATTR`、`CUSTOM_ELEMENT_HANDLING` 等)或放宽默认值之前,值得浏览一下[需要三思的标签和属性](https://github.com/cure53/DOMPurify/wiki/Security-Goals-&-Threat-Model#dangerous-tags-and-attributes-think-twice-before-allow-listing)——其中一些以不明显的方式存在危险。
### 常规设置
```js
// strip {{ ... }}, ${ ... } and <% ... %> to make output safe for template systems
// be careful please, this mode is not recommended for production usage.
// allowing template parsing in user-controlled HTML is not advised at all.
// only use this mode if there is really no alternative.
const clean = DOMPurify.sanitize(dirty, { SAFE_FOR_TEMPLATES: true });
// change how e.g. comments containing risky HTML characters are treated.
// be very careful, this setting should only be set to `false` if you really only handle
// HTML and nothing else, no SVG, MathML or the like.
// Otherwise, changing from `true` to `false` will lead to XSS in this or some other way.
const clean = DOMPurify.sanitize(dirty, { SAFE_FOR_XML: false });
```
### 控制我们的允许列表和阻止列表
```js
// allow only <b> elements, very strict
const clean = DOMPurify.sanitize(dirty, { ALLOWED_TAGS: ['b'] });
// allow only <b> and <q> with style attributes
const clean = DOMPurify.sanitize(dirty, {
ALLOWED_TAGS: ['b', 'q'],
ALLOWED_ATTR: ['style'],
});
// allow all safe HTML elements but neither SVG nor MathML
// note that the USE_PROFILES setting will override the ALLOWED_TAGS setting
// so don't use them together
const clean = DOMPurify.sanitize(dirty, { USE_PROFILES: { html: true } });
// allow all safe SVG elements and SVG Filters, no HTML or MathML
const clean = DOMPurify.sanitize(dirty, {
USE_PROFILES: { svg: true, svgFilters: true },
});
// allow all safe MathML elements and SVG, but no SVG Filters
const clean = DOMPurify.sanitize(dirty, {
USE_PROFILES: { mathMl: true, svg: true },
});
// change the default namespace from HTML to something different
const clean = DOMPurify.sanitize(dirty, {
NAMESPACE: 'http://www.w3.org/2000/svg',
});
// leave all safe HTML as it is and add <style> elements to block-list
const clean = DOMPurify.sanitize(dirty, { FORBID_TAGS: ['style'] });
// note: while <table> is allowed, <tbody> is always allowed too and cannot be
// forbidden - browsers insert it on their own, and forbidding it used to make
// sanitize() loop (see #365). Forbid 'table' as well to drop tables entirely.
// leave all safe HTML as it is and add style attributes to block-list
const clean = DOMPurify.sanitize(dirty, { FORBID_ATTR: ['style'] });
// extend the existing array of allowed tags and add <my-tag> to allow-list
const clean = DOMPurify.sanitize(dirty, { ADD_TAGS: ['my-tag'] });
// extend the existing array of allowed attributes and add my-attr to allow-list
const clean = DOMPurify.sanitize(dirty, { ADD_ATTR: ['my-attr'] });
// use functions to control which additional tags and attributes are allowed
const allowlist = {
one: ['attribute-one'],
two: ['attribute-two'],
};
const clean = DOMPurify.sanitize(
'<one attribute-one="1" attribute-two="2"></one><two attribute-one="1" attribute-two="2"></two>',
{
ADD_TAGS: (tagName) => {
return Object.keys(allowlist).includes(tagName);
},
ADD_ATTR: (attributeName, tagName) => {
return allowlist[tagName]?.includes(attributeName) || false;
},
}
); // <one attribute-one="1"></one><two attribute-two="2"></two>
// prohibit ARIA attributes, leave other safe HTML as is (default is true)
const clean = DOMPurify.sanitize(dirty, { ALLOW_ARIA_ATTR: false });
// prohibit HTML5 data attributes, leave other safe HTML as is (default is true)
const clean = DOMPurify.sanitize(dirty, { ALLOW_DATA_ATTR: false });
```
### 与自定义元素相关的控制行为
```js
// DOMPurify allows to define rules for Custom Elements. When using the CUSTOM_ELEMENT_HANDLING
// literal, it is possible to define exactly what elements you wish to allow (by default, none are allowed).
//
// The same goes for their attributes. By default, the built-in or configured allow.list is used.
//
// You can use a RegExp literal to specify what is allowed or a predicate, examples for both can be seen below.
// When using a predicate function for attributeNameCheck, it can optionally receive the tagName as a second parameter
// for more granular control over which attributes are allowed for specific elements.
// The default values are very restrictive to prevent accidental XSS bypasses. Handle with great care!
const clean = DOMPurify.sanitize(
'<foo-bar baz="foobar" forbidden="true"></foo-bar><div is="foo-baz"></div>',
{
CUSTOM_ELEMENT_HANDLING: {
tagNameCheck: null, // no custom elements are allowed
attributeNameCheck: null, // default / standard attribute allow-list is used
allowCustomizedBuiltInElements: false, // no customized built-ins allowed
},
}
); // <div is=""></div>
const clean = DOMPurify.sanitize(
'<foo-bar baz="foobar" forbidden="true"></foo-bar><div is="foo-baz"></div>',
{
CUSTOM_ELEMENT_HANDLING: {
tagNameCheck: /^foo-/, // allow all tags starting with "foo-"
attributeNameCheck: /baz/, // allow all attributes containing "baz"
allowCustomizedBuiltInElements: true, // customized built-ins are allowed
},
}
); // <foo-bar baz="foobar"></foo-bar><div is="foo-baz"></div>
const clean = DOMPurify.sanitize(
'<foo-bar baz="foobar" forbidden="true"></foo-bar><div is="foo-baz"></div>',
{
CUSTOM_ELEMENT_HANDLING: {
tagNameCheck: (tagName) => tagName.match(/^foo-/), // allow all tags starting with "foo-"
attributeNameCheck: (attr) => attr.match(/baz/), // allow all containing "baz"
allowCustomizedBuiltInElements: true, // allow customized built-ins
},
}
); // <foo-bar baz="foobar"></foo-bar><div is="foo-baz"></div>
// Example with attributeNameCheck receiving tagName as a second parameter
const clean = DOMPurify.sanitize(
'<element-one attribute-one="1" attribute-two="2"></element-one><element-two attribute-one="1" attribute-two="2"></element-two>',
{
CUSTOM_ELEMENT_HANDLING: {
tagNameCheck: (tagName) => tagName.match(/^element-(one|two)$/),
attributeNameCheck: (attr, tagName) => {
if (tagName === 'element-one') {
return ['attribute-one'].includes(attr);
} else if (tagName === 'element-two') {
return ['attribute-two'].includes(attr);
} else {
return false;
}
},
allowCustomizedBuiltInElements: false,
},
}
); // <element-one attribute-one="1"></element-one><element-two attribute-two="2"></element-two>
```
### 与 URI 值相关的控制行为
```js
// extend the existing array of elements that can use Data URIs
const clean = DOMPurify.sanitize(dirty, { ADD_DATA_URI_TAGS: ['a', 'area'] });
// extend the existing array of elements that are safe for URI-like values (be careful, XSS risk)
const clean = DOMPurify.sanitize(dirty, { ADD_URI_SAFE_ATTR: ['my-attr'] });
```
### 控制允许的属性值
```js
// allow external protocol handlers in URL attributes (default is false, be careful, XSS risk)
// by default only http, https, ftp, ftps, tel, mailto, callto, sms, cid, xmpp and matrix are allowed.
const clean = DOMPurify.sanitize(dirty, { ALLOW_UNKNOWN_PROTOCOLS: true });
// allow specific protocol handlers in URL attributes via regex (default is false, be careful, XSS risk)
// by default only (protocol-)relative URLs, http, https, ftp, ftps, tel, mailto, callto, sms, cid, xmpp and matrix are allowed.
// Default RegExp: /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i;
// The example below extends the default with one additional scheme (sftp).
// Keep the pattern linear-time: it runs against attacker-controlled values.
const clean = DOMPurify.sanitize(dirty, {
ALLOWED_URI_REGEXP:
/^(?:(?:(?:f|ht)tps?|sftp|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i,
});
```
### 影响返回类型
```js
// return a DOM HTMLBodyElement instead of an HTML string (default is false)
const clean = DOMPurify.sanitize(dirty, { RETURN_DOM: true });
// return a DOM DocumentFragment instead of an HTML string (default is false)
const clean = DOMPurify.sanitize(dirty, { RETURN_DOM_FRAGMENT: true });
// use the RETURN_TRUSTED_TYPE flag to turn on Trusted Types support if available
const clean = DOMPurify.sanitize(dirty, { RETURN_TRUSTED_TYPE: true }); // will return a TrustedHTML object instead of a string if possible
// use a provided Trusted Types policy
const clean = DOMPurify.sanitize(dirty, {
// supplied policy must define createHTML and createScriptURL
TRUSTED_TYPES_POLICY: trustedTypes.createPolicy('dompurify', {
createHTML(s) {
return s;
},
createScriptURL(s) {
return s;
},
}),
});
// opt out of DOMPurify's internal `dompurify` Trusted Types policy entirely
// (useful when your CSP `trusted-types` allowlist does not include `dompurify`)
const clean = DOMPurify.sanitize(dirty, { TRUSTED_TYPES_POLICY: null });
```
### 影响我们的清理方式
```js
// return entire document including <html> tags (default is false)
const clean = DOMPurify.sanitize(dirty, { WHOLE_DOCUMENT: true });
// disable DOM Clobbering protection on output (default is true, handle with care, minor XSS risks here)
const clean = DOMPurify.sanitize(dirty, { SANITIZE_DOM: false });
// enforce strict DOM Clobbering protection via namespace isolation (default is false)
// when enabled, isolates the namespace of named properties (i.e., `id` and `name` attributes)
// from JS variables by prefixing them with the string `user-content-`
const clean = DOMPurify.sanitize(dirty, { SANITIZE_NAMED_PROPS: true });
// keep an element's content when the element is removed (default is true)
const clean = DOMPurify.sanitize(dirty, { KEEP_CONTENT: false });
// glue elements like style, script or others to document.body and prevent unintuitive browser behavior in several edge-cases (default is false)
const clean = DOMPurify.sanitize(dirty, { FORCE_BODY: true });
// remove all <a> elements under <p> elements that are removed
const clean = DOMPurify.sanitize(dirty, {
FORBID_CONTENTS: ['a'],
FORBID_TAGS: ['p'],
});
// extend the default FORBID_CONTENTS list to also remove <a> elements under <p> elements
const clean = DOMPurify.sanitize(dirty, {
ADD_FORBID_CONTENTS: ['a'],
FORBID_TAGS: ['p'],
});
// change the parser type so sanitized data is treated as XML and not as HTML, which is the default
const clean = DOMPurify.sanitize(dirty, {
PARSER_MEDIA_TYPE: 'application/xhtml+xml',
});
```
### 我们进行清理时的影响
```js
// use the IN_PLACE mode to sanitize a node "in place", which is much faster depending on how you use DOMPurify
const dirty = document.createElement('a');
dirty.setAttribute('href', 'javascript:alert(1)');
const clean = DOMPurify.sanitize(dirty, { IN_PLACE: true }); // see https://github.com/cure53/DOMPurify/issues/288 for more info
```
关于 `IN_PLACE` 需要了解几点:
- 你传入的根节点本身必须是允许的标签,且不能被 DOM 覆盖(例如一个 `<form>` 拥有名为 `nodeName` 或 `ownerDocument` 的子节点)。如果出现这种情况,DOMPurify 会剥离根节点子树中所有不在允许列表中的属性,然后抛出 `TypeError`,因此被拒绝的根节点绝不会以武装状态被交还。
- 如果遍历过程中有任何异常抛出,同样的失败关闭式中和处理会在错误传播之前,对根节点以及该次遍历中已经分离的每个子树执行。
- 被钩子从树中分离的节点(一种常见模式,参见 [Hooks](#hooks))会被视为已移除。在 `IN_PLACE` 模式下,`sanitize()` 开始时位于根节点之下、而在调用结束时不再位于其下的每个元素,都会在 `sanitize()` 返回或抛出之前剥离其不在允许列表中的属性,无论是什么将其分离:钩子移除它所针对的节点、其父节点或其他祖先节点,或树中其他位置的节点;钩子将子树移出根节点;或自定义元素对 DOMPurify 自身的某个属性移除做出反应。因此,在你构建活动树时已经在加载的 `` 无法在 `sanitize()` 返回后触发。同样,你的树中某个节点被钩子或自定义元素移动到遍历已经经过的位置时,它仍会在 `sanitize()` 返回之前被净化;如果树持续以这种方式变化,DOMPurify 会剥离根节点至空并抛出异常,而不是将其返回。
- DOMPurify 无法撤销在调用 `sanitize()` _之前_ 已经触发的引擎变更(连接时应用的补丁、`selectedcontent` 重新克隆等等)。请在将攻击者控制的树连接到活动文档之前对其进行净化,而不是之后。
这里还有[更多示例](https://github.com/cure53/DOMPurify/tree/main/demos#what-is-this),展示了如何运行、自定义和配置 DOMPurify 以满足你的需求。
## 持久化配置
与其反复向 `DOMPurify.sanitize` 传递相同的配置,你可以使用 `DOMPurify.setConfig` 方法。你的配置将持续生效,直到你下一次调用 `DOMPurify.setConfig`,或直到你调用 `DOMPurify.clearConfig` 将其重置。请记住,只有一个活动配置,这意味着一旦设置,传递给 `DOMPurify.sanitize` 的所有额外配置参数都会被忽略。
## 钩子
DOMPurify 允许你通过 `DOMPurify.addHook` 方法将一个或多个函数附加到以下钩子之一,以增强其功能:
- `beforeSanitizeElements`
- `uponSanitizeElement`(没有 's'——对每个元素调用)
- `afterSanitizeElements`
- `beforeSanitizeAttributes`
- `uponSanitizeAttribute`
- `afterSanitizeAttributes`
- `beforeSanitizeShadowDOM`
- `uponSanitizeShadowNode`
- `afterSanitizeShadowDOM`
它会将当前处理的 DOM 节点、在需要时经过验证的节点和属性数据字面量以及 DOMPurify 配置传递给回调。查看 [MentalJS 钩子演示](https://github.com/cure53/DOMPurify/blob/main/demos/hooks-mentaljs-demo.html) 了解如何优雅地使用该 API。
_示例_:
```js
DOMPurify.addHook(
'uponSanitizeAttribute',
function (currentNode, hookEvent, config) {
// Do something with the current node
// You can also mutate hookEvent for current node (i.e. set hookEvent.forceKeepAttr = true)
// For other than 'uponSanitizeAttribute' hook types hookEvent equals to null
}
);
```
### 值得了解的 Hook 行为
- **支持将节点从 hook 中分离。** 如果 `beforeSanitizeElements` 或 `uponSanitizeElement` hook 将当前节点从树中移除(例如通过 `node.remove()` 移除 `foreignObject`),DOMPurify 会将该节点视为已移除并停止处理它。此类节点不会被记录在 `DOMPurify.removed` 中。在 `IN_PLACE` 模式下,被分离的子树仍会被中和处理(自 3.4.13 起),因为活动节点可能携带已排队的资源事件。
---
[Read more](https://github.com/cure53/dompurify)