必须用 DOMPurify.sanitize() 等专业库净化 HTML 后再渲染,因 v-html 和 dangerouslySetInnerHTML 本身不校验、不剥离,直接执行恶意代码;正则清洗不可靠,服务端净化后前端仍需二次校验。

直接用 v-html 或 dangerouslySetInnerHTML 渲染不可信 HTML,等于主动交出 DOM 控制权——XSS 不是“可能”,而是“必然发生”。
为什么原生 v-html 和 dangerouslySetInnerHTML 不能当过滤器用
这两个 API 的设计目标就是“跳过框架的模板编译,直写 innerHTML”,它们本身不解析、不校验、不剥离。传入 <img src=x onerror=alert(1)>,浏览器就会执行 alert(1)。
-
v-html在 Vue 3 中仍完全绕过响应式系统和模板编译器,等价于el.innerHTML = rawString -
dangerouslySetInnerHTML的名字已警告:React 明确拒绝提供内置过滤能力 - 正则替换
on\w+=或删script标签?绕过方式太多(大小写混淆、注释分隔、UTF-8 编码、data: URL 等)
Vue 项目中安全渲染 HTML 的实操路径
不要自己写白名单逻辑。优先使用成熟、持续维护的解析器,并严格限定输出范围。
React 与 Next.js 性能优化指南,源自 Vercel 工程团队。适用于编写、审查或重构 React/Next.js 代码时使用。
- 用
DOMPurify.sanitize()处理原始字符串,再喂给v-html:import DOMPurify from 'dompurify';<br>const clean = DOMPurify.sanitize(dirtyHtml, {<br> ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p', 'br', 'ul', 'ol', 'li'],<br> ALLOWED_ATTR: ['class'],<br>});<br>return { __html: clean }; - 避免在
setup()中反复调用DOMPurify.sanitize();对高频内容做记忆化(computed或ref缓存) - 禁用
ADD_TAGS和ADD_ATTR,除非你明确知道新增标签/属性的全部 XSS 向量(比如iframe的sandbox属性也需手动放行)
React 项目中替代 dangerouslySetInnerHTML 的安全方案
不要试图用 useMemo + 正则“清洗” HTML 字符串——正则无法可靠解析嵌套标签与编码边界。
立即学习“前端免费学习笔记(深入)”;
- 服务端已做过净化?前端仍需二次校验:用
DOMPurify.sanitize()再过一遍,配置同 Vue 场景 - 若内容来自富文本编辑器(如 TipTap、Slate),应直接消费其结构化 JSON 输出,而非导出 HTML 后再渲染
- 必须用 HTML 片段时,用
createPortal+DOMPurify创建隔离容器,避免污染主组件作用域
容易被忽略的三个高危细节
过滤机制失效往往不出现在主流程,而卡在边缘环节:
- 后端返回的 HTML 若含
data-*属性,且前端用getAttribute('data-xxx')提取后拼进模板,会逃逸过滤范围 -
DOMPurify默认不处理style属性中的 CSS 表达式(如expression(...)),需显式启用SAFE_FOR_TEMPLATES: true - Vue 的
v-html绑定值若为响应式对象字段(如state.content),但该字段被异步更新后未重新 sanitize,旧脏数据可能残留


















