富文本输出的html不能直接innerhtml渲染,因其含冗余标签、残留属性(如data-mce-*)、非法嵌套(文本)、不可见字符(如zwsp)及xss风险点(onerror、javascript:等),导致解析异常、布局错乱、css失效;必须先dom克隆+白名单过滤,再经dompurify二次净化,并嵌入数据流各环节持续清洗。

富文本输出的 HTML 为什么不能直接 innerHTML 渲染
因为编辑器输出的 HTML 天然含冗余标签、残留属性(data-mce-*、class="Apple-style-span")、非法嵌套(<p></p>
<div>文本</div>
onerror、javascript: 或内联 style 注入点。
用 DOMPurify 做清洗前必须先克隆节点
直接对 editor.root.innerHTML 或 contenteditable 元素调用 DOMPurify.sanitize() 是无效的——DOMPurify 输入的是字符串,而原始 HTML 字符串里已混入浏览器自动修正的结构、转义失败的字符、甚至注释中隐藏的恶意片段。正确做法是:
- 先
const clone = el.cloneNode(true)获取干净 DOM 树 - 遍历移除所有非白名单属性:
class、style、id、data-* - 只保留语义化标签:
p、h1–h6、ul/ol/li、strong、em、a、img - 再把
clone.innerHTML传给DOMPurify.sanitize(),并显式配置ALLOWED_TAGS和ALLOWED_ATTR
Quill/TinyMCE/CKEditor5 输出 HTML 的关键清洗时机
不同编辑器暴露内容的方式差异很大,清洗不能“一刀切”:
-
Quill:别用root.innerHTML,优先调用editor.getSemanticHTML()(需插件)或editor.clipboard.convert();若必须导出 HTML,确保已禁用clipboard.matchers中的危险转换逻辑 -
TinyMCE:启用valid_elements: "p,strong,em,a[href],img[src|alt]"+forced_root_block: "p",避免输出div段落 -
CKEditor5:禁用htmlSupport插件,改用editor.data.get({ trim: 'both' });若必须支持自定义 HTML,需配套编写完整的upcast/downcast规则,否则仍会被丢弃
React 中渲染富文本 HTML 的唯一安全路径
dangerouslySetInnerHTML 不是“危险但可用”,而是“仅当内容已通过服务端+前端双重净化后才可触达”。实际操作中必须满足:
- 前端清洗后,仍需服务端用
Cheerio或jsdom二次解析:检查是否含/、是否有孤立<img>、是否所有纯文本都被<p></p>包裹 - 禁止在组件内做任何“动态拼接 HTML 字符串”的行为,包括
innerHTML += ...或document.write() - 若内容来自用户提交,且无法保证清洗完整性,降级为
textContent渲染,或强制转 Markdown 再用安全解析器(如remark)转 HTML
最易被忽略的一点:清洗不是一次性的动作。用户粘贴、编辑器自动补全、服务端回填、前端 diff 更新,每个环节都可能引入新污染。清洗逻辑必须嵌入到数据流入口,而非仅放在最终渲染前。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











