DOMPurify.sanitize() 更适合沙箱环境,因为它基于真实 HTML 解析器重建 DOM,能识别并清除内联事件、危险协议、嵌套混淆等 XSS 载荷;而 strip_tags() 仅字符串替换,无法防御 onerror 等属性型攻击。

DOMPurify.sanitize() 为什么比 strip_tags() 更适合沙箱环境
因为 strip_tags() 只做字符串层面的标签剥离,完全不解析 DOM 结构,对 onclick、javascript:、onerror 等属性毫无感知。在沙箱中,攻击者只需写 <img src=x onerror=alert(1)> 就能绕过——它没标签名可删,但事件照样执行。
DOMPurify.sanitize() 则基于真实 HTML 解析器重建 DOM,能识别:
- 内联事件属性(自动删除所有
on*属性) - 危险协议(
javascript:、vbscript:、data:开头的href/src) - 嵌套混淆(如
<scr<script>ipt>或实体编码<script></script>) - 注释、CDATA、自闭合标签等边界情况
它不是“删字符串”,而是“重建安全子树”——这才是沙箱该有的行为粒度。
配置 ALLOWED_TAGS 时必须同步约束 ALLOWED_ATTR 和 FORBID_ATTR
只开白名单标签不等于安全。比如允许 <a> 却不限制 href,攻击者就能写 <a href="javascript:fetch('/api/token')">点我</a>;允许 <img> 却放行 onerror,就等于开了执行入口。
立即学习“前端免费学习笔记(深入)”;
实操建议:
- 显式声明
ALLOWED_ATTR: ['href', 'src', 'alt', 'title'],不依赖默认值 - 用
FORBID_ATTR: ['onerror', 'onload', 'onclick', 'onmouseover']双保险 - 对
href加协议白名单:ADD_URI_SAFE_PROTOCOL: ['http:', 'https:', '/'] - 禁用
style属性(除非你真需要内联样式且已校验 CSS 值)
Sanitizer API 的 sanitizeFor() 容器类型参数不能乱填
new Sanitizer().sanitizeFor('div', html) 和 sanitizeFor('span', html) 行为不同——前者允许块级结构嵌套,后者会把 <p> 当非法标签降级或剔除。这不是 bug,是语义上下文控制。
常见错误:
- 统一用
'div'导致富文本编辑器里粘贴的<strong><em>嵌套被意外截断 - 往
<span>内插入时用了'div',结果浏览器把<p>强转成<div>,破坏行内语义 - 调用后直接赋值
el.innerHTML = sanitizer.sanitizeFor(...)——返回的是DocumentFragment,不是字符串,会静默失败
正确做法:
- 根据目标容器选参数:
article、section、span,别图省事全用div - 插入 DOM 用
el.replaceChildren(frag);要字符串再取frag.body.innerHTML(仅当确定内容在<body>下) - 必须运行时检测:
if (typeof Sanitizer !== 'undefined'),否则 Safari/Firefox/Node.js 会报ReferenceError
contenteditable 粘贴清洗必须拦截 paste 事件,不能靠 input 或 blur
用户 Ctrl+V 后,浏览器会在 paste 事件触发前就把原始 text/html 插入到 DOM 中——此时 input 或 blur 已经晚了。你看到的“污染”已经是既成事实。
关键动作只有三步:
- 监听
paste并立即event.preventDefault() - 用
event.clipboardData.getData('text/html')拿原始 HTML(Safari 返回空时 fallback 到text/plain并清理零宽字符\u200b) - 用
DOMParser解析 + 白名单遍历清洗,最后用range.insertNode()插入光标位置
别信 contenteditable="plaintext-only":Chrome/Firefox 完全忽略它,Safari 虽支持但只禁格式不防 XSS,仍需手动清洗。



















