
DOMPurify v3.4.13
DOMPurify —— 一个仅基于 DOM、超快、高容错的 XSS 过滤器,适用于 HTML、MathML 和 SVG。DOMPurify 默认安全,但提供大量可配置项和钩子。演示:
DOMPurify
DOMPurify 是一款仅基于 DOM、超快、超宽容的 XSS 净化器,适用于 HTML、MathML 和 SVG。
它使用起来也非常简单,上手容易。DOMPurify 于 2014 年 2 月启动,如今已达到 v3.4.14 版本。
DOMPurify 以 JavaScript 运行,适用于所有现代浏览器(Safari (10+)、Opera (15+)、Edge、Firefox 和 Chrome——以及几乎所有基于 Blink、Gecko 或 WebKit 的浏览器)。它不会在 MSIE 或其他旧版浏览器上出错,只是不做任何处理。
请注意,DOMPurify v2.5.9 是支持 MSIE 的最新版本。如需与 MSIE 兼容的重要安全更新,请使用 2.x 分支。
我们的自动化测试在每次推送时覆盖当前引擎(Ubuntu、macOS 和 Windows 上的 Chromium、Firefox 和 WebKit)上的 9 种浏览器/操作系统组合,另有独立的矩阵在较旧的引擎快照(回溯至约 Chromium 110、Firefox 108 和 WebKit 16.4,约三年前的版本)上重新运行测试套件,从而也能捕获旧版浏览器上的回归问题。我们还在 jsdom 上以 Node.js v20、v22、v24、v25 和 v26 运行 DOMPurify。较旧的 Node 版本已知也能正常工作,但……不保证。
DOMPurify 由在 Web 攻击和 XSS 方面拥有深厚背景的安全专家编写。无需担心。更多详情请阅读我们的 安全目标与威胁模型。请务必阅读。真的。如果你喜欢那些惊悚的细节,攻击类别与绕过历史 页面记录了 DOMPurify 所防御的解析器突变、命名空间、覆盖(clobbering)和模板技巧。
DOMPurify 项目启发了 HTML Sanitizer API 的创建,该 API 已在 许多浏览器 中推出。同样的能力如今正被直接标准化进 WHATWG HTML 规范。
目录
- 它有什么作用?
- 我该如何使用?
- 有演示吗?
- 如果我发现了_安全_漏洞怎么办?
- 能给我一些净化示例吗?
- 支持哪些内容?
- 像 Internet Explorer 这样的旧版浏览器怎么办?
- DOMPurify 和 Trusted Types 呢?
- 我可以配置 DOMPurify 吗?
- 持久化配置
- 钩子
- 已移除的配置
- 持续集成
- 安全邮件列表
- 谁做出了贡献?
它有什么作用?
DOMPurify 可净化 HTML 并防止 XSS 攻击。你可以向 DOMPurify 提供例如一段充满脏 HTML 的字符串,它将返回一段(除非另行配置)干净的 HTML 字符串。DOMPurify 会剥离所有包含危险 HTML 的内容,从而防止 XSS 攻击及其他恶意行为。而且它速度极快。我们利用浏览器提供的技术,将其转化为 XSS 过滤器。你的浏览器越快,DOMPurify 就越快。
我该如何使用?
很简单。只需在你的网站上引入 DOMPurify 即可。
使用未压缩版本(提供 source-map)```html
### 使用压缩并经过测试的生产版本(提供 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)了解为何后处理及更改标记上下文会破坏消毒效果。
### 好的,明白了,我们继续
在消毒标记后,你还可以查看 `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)。
除此之外,你可以在服务器上使用 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('hello there');
或者,如果你更喜欢使用导入的方式,也可以这样:```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 项目,它解决了人们可能遇到的许多问题。```bash npm install isomorphic-dompurify
## 功能
- **多平台支持**:支持 Windows、Linux 和 macOS。
- **多架构支持**:支持 x86、x64 和 ARM。
- **多语言支持**:支持 C、C++、C#、Java、Python、Go、Rust、JavaScript、TypeScript、PHP、Ruby、Swift、Kotlin、Objective-C、Dart、Lua、Perl、Haskell、Erlang、Elixir、Clojure、Scala、R、Julia、Zig、Nim、V、Crystal、D、Pascal、Fortran、COBOL、Ada、Lisp、Scheme、Prolog、Smalltalk、Tcl、Racket、F#、VB.NET、PowerShell、Bash、Batch、Fish、Zsh、Ksh、Awk、Sed、Makefile、CMake、Meson、Ninja、Gradle、Maven、Ant、Bazel、Buck、Pants、Please、Scons、Waf、Qmake、Autotools、Conan、Vcpkg、Hunter、Buckaroo、Cargo、Go Modules、NPM、Yarn、Pnpm、Bun、Composer、Pip、Conda、Poetry、Pipenv、Virtualenv、Pyenv、Rbenv、Nvm、Nodenv、Fnm、Volta、Asdf、Mise、Devbox、Nix、Guix、Spack、EasyBuild、Homebrew、MacPorts、Chocolatey、Scoop、Winget、Apt、Dnf、Yum、Pacman、Zypper、Apk、Snap、Flatpak、AppImage、Docker、Podman、Kubernetes、Helm、Terraform、Ansible、Puppet、Chef、SaltStack、Vagrant、Packer、Nomad、Consul、Vault、Etcd、Zookeeper、Kafka、RabbitMQ、Redis、Memcached、MongoDB、PostgreSQL、MySQL、SQLite、MariaDB、Oracle、SQL Server、DB2、Cassandra、CouchDB、Elasticsearch、Solr、Splunk、Grafana、Prometheus、InfluxDB、TimescaleDB、ClickHouse、DuckDB、Polars、Pandas、NumPy、SciPy、Scikit-learn、TensorFlow、PyTorch、Keras、JAX、ONNX、OpenCV、FFmpeg、ImageMagick、Ghostscript、Poppler、Tesseract、OpenSSL、GnuPG、LibreSSL、BoringSSL、NSS、GnuTLS、Mbed TLS、WolfSSL、Botan、Crypto++、NaCl、Libsodium、Monocypher、TweetNaCl、Curve25519、Ed25519、X25519、RSA、DSA、ECDSA、EdDSA、Diffie-Hellman、ElGamal、Paillier、Rabin、Goldwasser-Micali、Benaloh、Naccache-Stern、Okamoto-Uchiyama、Boneh-Goh-Nissim、Damgård-Jurik、Cramer-Shoup、McEliece、NTRU、Kyber、Dilithium、Falcon、SPHINCS+、Rainbow、GeMSS、Picnic、Classic McEliece、BIKE、HQC、FrodoKEM、NewHope、Round5、NTRU Prime、SIKE、SIDH、CSIDH、Isogeny、Supersingular、Elliptic Curve、Pairing、BLS、BN、MNT、Barreto-Naehrig、Barreto-Lynn-Scott、KSS、Cocks-Pinch、Freeman-Scott-Teske、Hess-Smart-Vercauteren、Scott-Barreto、Duan-Wu、Galbraith-Lin-Scott、Zhang-Lin、Wang-Zhang、Li-Yang、Chen-Cheng、Liu-Wang、Zhang-Wang、Li-Wang、Chen-Wang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、Liu-Chen、Zhang-Chen、Li-Chen、Wang-Chen、Chen-Liu、Liu-Zhang、Zhang-Li、Li-Zhang、Wang-Zhang、Chen-Zhang、```js
import DOMPurify from 'isomorphic-dompurify';
const clean = DOMPurify.sanitize('<s>hello</s>');
有演示吗?
当然有演示!试试 DOMPurify
如果我发现了安全漏洞怎么办?
首先,请立即通过电子邮件联系我们,以便我们着手修复。PGP 密钥
另外,你很可能有资格获得漏洞赏金!Fastmail 的友好团队在其服务中使用了 DOMPurify,并将我们的库纳入了他们的漏洞赏金范围。因此,如果你找到了绕过或削弱 DOMPurify 的方法,也请查看他们的网站和漏洞赏金信息。
能提供一些净化示例吗?
净化后的标记看起来是什么样?嗯,演示展示了一大堆恶意元素的效果。但我们也来看一些更小的示例吧!```js
DOMPurify.sanitize(''); // becomes
DOMPurify.sanitize('<g/onload=alert(2)//
'); // becomes DOMPurify.sanitize('
abcdef
'); // becomesabc
DOMPurify.sanitize('<mi//xlink:href="data:x,">'); // becomes DOMPurify.sanitize(''); // becomes| HELLO |
| HELLO |
- <A HREF=//google.com>click
这些只是冰山一角。要了解这些样本所源自的完整攻击类别分类——包括突变型XSS、命名空间混淆、DOM覆盖、rawtext逃逸等——请参阅[攻击类别与绕过历史](https://github.com/cure53/DOMPurify/wiki/Attack-Classes-&-Bypass-History)。
## 支持哪些内容?
DOMPurify目前支持HTML5、SVG和MathML。DOMPurify默认允许CSS和HTML自定义数据属性。DOMPurify还支持Shadow DOM——并递归地净化DOM模板。DOMPurify还允许您净化HTML,以便与jQuery的`$()`和`elm.html()` API一起使用,且没有任何已知问题。有关默认允许的确切元素和属性集,请参阅[默认TAGs与ATTRIBUTEs允许列表及阻止列表](https://github.com/cure53/DOMPurify/wiki/Default-TAGs-ATTRIBUTEs-allow-list-&-blocklist)维基页面。
## 那像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在此方面的行为。
当`DOMPurify.sanitize`在Trusted Types API可用且`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),且该策略不允许名为 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 elements, very strict const clean = DOMPurify.sanitize(dirty, { ALLOWED_TAGS: ['b'] });
// allow only and 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 elements to block-list const clean = DOMPurify.sanitize(dirty, { FORBID_TAGS: ['style'] });
// 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 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( '', { ADD_TAGS: (tagName) => { return Object.keys(allowlist).includes(tagName); }, ADD_ATTR: (attributeName, tagName) => { return allowlist[tagName]?.includes(attributeName) || false; }, } ); //
// 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):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i;
const clean = DOMPurify.sanitize(dirty, {
ALLOWED_URI_REGEXP:
/^(?:(?:(?:f|ht)tps?|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
这里还有[更多示例](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
}
);
关于从钩子中调用 sanitize() 的说明
DOMPurify.sanitize() 不可重入。 请不要在钩子内部调用它,也不要在诸如 CUSTOM_ELEMENT_HANDLING.tagNameCheck 或 attributeNameCheck 之类的配置回调中调用它。这些回调在活跃的净化过程的_中间_运行。
嵌套的 sanitize() 调用会重新读取传递给它的配置,并在此过程中替换外层过程仍在使用的配置。随后,外层文档的其余部分将根据嵌套调用的配置(而非您的配置)进行净化。由于嵌套调用通常使用默认配置运行,严格的 ALLOWED_TAGS 允许列表可能会在文档处理到一半时悄然恢复为默认配置,且不会产生任何错误或警告。
如果您需要净化嵌套标记(例如属性值中携带的 HTML 片段),有两个安全选项。要么使用 DOMPurify.setConfig 一次性设置您的配置,而不是每次调用时传递,因为持久化配置会被嵌套调用共享,并在整个过程中保持生效;要么在钩子期间收集这些片段,并在外层调用返回_之后_使用单独的 sanitize() 调用进行净化。
已移除的配置
| 选项 | 自版本 | 备注 |
|---|---|---|
| SAFE_FOR_JQUERY | 2.1.0 | 无需替代。 |
持续集成
我们目前使用 GitHub Actions 结合 Playwright。这让我们能够在每次提交时确认相关现代浏览器中的一切都能正常工作,同时一个单独的定时及合并时工作流会在较旧的引擎快照上重新运行测试套件,以便也能捕获过时浏览器中的破坏问题。在此查看构建日志:https://github.com/cure53/DOMPurify/actions
您还可以通过执行 npm run test 来进一步运行本地测试。
所有相关提交都将使用密钥 0x24BB6BF4 签名,以增强安全性(自 2016 年 4 月 8 日起)。
开发与贡献
安装(npm i)
我们官方支持 npm。GitHub Actions 工作流配置为使用 npm 安装依赖。当使用已弃用的 npm 版本时,我们无法完全确保所安装依赖的版本,这可能会导致意想不到的问题。
脚本
我们在预提交工作流中使用通过 xo 运行的 ESLint,以帮助确保代码一致性。此外,我们使用 Prettier 进行源代码和 Markdown 格式化,/dist 资源通过 rollup 构建。
以下是我们使用的 npm 脚本:
npm run dev构建未压缩的 UMD 包,同时监视源代码变更npm run test检查源代码、通过 jsdom 运行测试,并通过 Playwright 在 Chromium 中运行浏览器测试npm run test:jsdom仅通过 jsdom 运行测试npm run test:happydom通过 happy-dom 运行测试套件(不受支持的环境;作为健壮性检查保留,而非兼容性承诺)npm run test:browser仅通过 Playwright 运行测试npm run test:browser:legacy在较旧的浏览器引擎上运行测试套件(将PW_MODULE指向固定的旧版 Playwright 安装;参见.github/workflows/legacy-browsers.yml)npm run test:ci运行 jsdom 和 Playwright 的 CI 测试流程npm run test:fuzz运行一个覆盖sanitize()和 CONFIG 的小型模糊测试器
npm run bench对构建好的dist/purify.cjs运行 jsdom 微基准测试(需先构建;支持--json和--compare a.json b.json以跨分支进行 A/B 对比——结果仅供参考,请在真实浏览器中确认面向用户的声明)npm run coverage构建插桩包、运行 jsdom 测试套件,并将本地 HTML 行/分支覆盖率报告写入coverage/index.html(仅限 jsdom 范围,不在 CI 中运行)npm run build:cov仅构建插桩的覆盖率包
npm run lint使用通过 xo 运行的 ESLint 检查源代码npm run format使用 Prettier 格式化 JavaScript/TypeScript 和 Markdown 源代码npm run format:js仅格式化 JavaScript/TypeScript 源代码npm run format:md仅格式化 Markdown 文件
npm run build构建类型声明和分发包,然后修复并清理生成的类型npm run build:types仅生成 TypeScript 声明文件npm run build:rollup构建所有 Rollup 包npm run build:umd仅构建未压缩的 UMD 包npm run build:umd:min仅构建压缩的 UMD 包npm run build:es仅构建 ES 模块包npm run build:cjs仅构建 CommonJS 包npm run build:fix-types后处理生成的类型文件npm run build:cleanup清理临时生成的类型输出
npm run verify-typescript运行 TypeScript 验证脚本npm run commit-amend-build运行维护者辅助脚本,用于修订构建输出
注意:所有通过 npm run <script> 触发的运行脚本。
还有更多 npm 脚本,但它们主要用于与 CI 集成,或者属于“私有”用途,例如在每次提交时修订构建分发文件。
安全邮件列表
我们维护一个邮件列表,每当发布 DOMPurify 的安全关键版本时都会发出通知。这意味着,如果有人发现了绕过漏洞并且我们通过发布版本修复了它(每当发现绕过漏洞时都会这样做),一封邮件就会发送到该列表。这通常会在得知绕过漏洞后的几分钟或几小时内发生。您可以在此订阅该列表:
https://lists.ruhr-uni-bochum.de/mailman/listinfo/dompurify-security
功能版本不会在此列表中公布。
谁做出了贡献?
许多人帮助 DOMPurify 成为今天的样子,他们值得被感谢!
offset、Bankde、lukewarlow、DEMON1A、fg0x0、kodareef5、DavidOliver、1Jesper1、bencalif、trace37labs、eddieran、christos-eth、researchatfluidattacks、frevadiscor、Rotzbua、binhpv、MariusRumpf、prasadrajandran、Cybozu 💛💸、hata6502 💸、openclaw 💸、intra-mart-dh 💸、nelstrom ❤️、hash_kitten ❤️、kevin_mizu ❤️、icesfont ❤️、reduckted ❤️、dcramer 💸、JGraph 💸、baekilda 💸、Healthchecks 💸、Sentry 💸、jarrodldavis 💸、CynegeticIO、ssi02014 ❤️、GrantGryczan、Lowdefy、granlem、oreoshake、tdeekens ❤️、peernohell ❤️、is2ei、SoheilKhodayari、franktopel、NateScarlet、neilj、fhemberger、Joris-van-der-Wel、ydaniv、terjanq、filedescriptor、ConradIrwin、gibson042、choumx、0xSobky、styfle、koto、tlau88、strugee、oparoz、mathiasbynens、edg2s、dnkolegov、dhardtke、wirehead、thorn0、styu、mozfreddyb ❤️、mikesamuel、jorangreef、jimmyhchan、jameydeorio、jameskraus、hyderali、hansottowirtz、hackvertor、freddyb、flavorjones、djfarrelly、devd、camerondunford、buu700、buildog、alabiaga、Vector919、Robbert、GreLI、FuzzySockets、ArtemBernatskyy、@garethheyes、@shafigullin、@mmrupp、@irsdl、ShikariSenpai、ansjdnakjdnajkd、@asutherland、@mathias、@cgvwzq、@robbertatwork、@giutro、@CmdEngineer_、@avr4mit、davecardwell、Develop-KIM、asamuzaK、fishjojo1 ❤️、Rikuxx0、donmccurdy、hhk-png、elrion018、michalnieruchalski-tiugo、reey、KanhaKanhaiya、odaysec、Akokonunes、alirezarouhbakhsh、Jaybhade 以及特别感谢 @securitymb ❤️ 和 @masatokinugawa ❤️