HTML脏数据清洗必须显式过滤style、class、data-及内联事件属性,因白名单工具仅校验标签与属性名,不校验属性值内容;如style中javascript:、class中布局类、data-中敏感字段或on*事件均可能引发XSS或样式异常。

HTML脏数据清洗不能只删标签,属性层污染才是隐藏最深的雷——style、class、onclick、data-类自定义属性,不清理就存库或渲染,轻则样式炸裂,重则XSS漏网。
为什么style和class必须显式过滤,而不是靠白名单标签自动带过
ponytail 或 sanitize-html 这类工具默认只管标签名和属性名,不管属性值内容。比如 <div class="text-red-500 font-bold" style="color: red; background: url(javascript:alert(1))">,即使你白名单放行了 <code>div 和 class、style 属性,里面的 javascript: 依然能执行;而 Tailwind 类名看似安全,实则可能被后端模板注入恶意 CSS 变量。
- 真正干净的
class应该是空值,或仅限极少数语义化类(如highlight、quote),不能放行整套 UI 框架类名 -
style值必须做二次校验:只允许color、font-weight等有限属性,且值需匹配正则^#?[0-9a-fA-F]{3,6}|[0-9]+(px|em|%|rem)?$,禁止url()、expression()、calc()等可执行上下文 - 浏览器原生解析会忽略部分非法
style值,但服务端 DOM 解析器(如 jsdom)可能照单全收,导致 SSR 渲染时触发
data-属性要不要留?留哪些?
data- 属性本身不危险,但它是前端逻辑和后端数据耦合的高发区。很多 CMS 导出 HTML 时会把原始 ID、版本号、编辑状态塞进 data-id、data-version、data-draft,这些信息不该暴露给终端用户,也不该参与富文本渲染流程。
- 默认策略:全部清除。除非业务明确要求保留某几个,比如
data-ref用于锚点跳转、data-lang用于多语言标记 - 若需保留,必须加前缀白名单,例如只放行
data-ref和data-lang,拒绝data-user-id、data-src等泛用型字段 - 注意
data-后面跟的是任意字符串,正则匹配要写成^data-(ref|lang)$,不能写成^data-.*$,否则等于没设防
内联事件属性(onclick、onmouseover等)怎么防漏?
这类属性哪怕值为空、为 return false 或只是个变量名,也属于高危项。ponytail 的默认配置通常已禁用所有 on* 属性,但如果你手动扩展了白名单,或者用了旧版配置,极易遗漏。
立即学习“前端免费学习笔记(深入)”;
- 必须在白名单中显式排除:
onclick、onload、onerror、onfocus、onsubmit……完整列表参考 MDN 的 HTML 全局事件属性页 - 注意大小写:有些爬虫输出的 HTML 里是
ONCLICK或OnCliCk,清洗器若只匹配小写会漏掉,ponytail 默认不区分大小写,但自定义正则需加i标志 - 更稳妥的做法是:用否定式白名单,例如允许所有属性名,但排除匹配
^on[a-z]+的,比逐个列更可靠
清洗后还要检查什么?一个常被跳过的验证动作
清洗完别急着入库或渲染,先用 DOMParser(Node 端可用 jsdom)重新 parse 一遍输出 HTML,再遍历所有元素的 attributes 集合,人工确认是否还有残留的 style 值含 javascript:、class 含 hidden 或 absolute 等布局类、data- 属性超出白名单范围——这种“清洗后二次扫描”动作,能发现 80% 的配置疏漏。
尤其要注意那些看似无害的属性值,比如 class="u-hidden" 在某些 UI 库里会触发 display: none !important,导致正文段落莫名消失;又比如 style="opacity: 0" 虽不执行 JS,但会让内容不可见,等于变相丢数据。



















