HTML安全需分层卡控输入、解析、输出三环节,单点过滤90%失效;escapeHtml仅覆盖部分路径,自定义插件、v-pre区域、Markdown片段易绕过;白名单须严格限制危险属性及协议;不同输出上下文需匹配对应编码方式,不可统一用HTML转义。

HTML代码质量审查不是“加个转义函数就完事”,而是要分层卡控输入、解析、输出三个环节。只做单点过滤,90%的XSS漏洞仍会漏过。
escapeHtml 函数调用是否覆盖所有渲染路径
docsify 的 escapeHtml 确实能转义 <、>、"、'、&,但它只在特定位置被调用——比如 src/core/render/utils.js 中的文本节点处理,或 media.js 里对 iframe src 的赋值:html:`<iframe src="${escapeHtml(url)}"${attrs}></iframe>`。
容易踩的坑:
- 开发者自定义插件中直接拼接 HTML 字符串,绕过了 core 渲染流程,
escapeHtml完全不生效 -
v-pre或data-ignore标记区域被跳过解析,用户输入原样插入 DOM - Markdown 解析后生成的 HTML 片段(如表格、代码块)若未走统一净化通道,可能含未转义属性
HTML 标签白名单是否严格限制了危险属性
仅过滤标签(如禁止 <script>)远远不够。攻击者更常利用 <img src=x onerror=alert(1)> 这类“合法”标签+危险事件属性的方式触发 XSS。
立即学习“前端免费学习笔记(深入)”;
审查重点应落在属性级控制:
- 是否禁用全部事件处理器:
onerror、onclick、onload、onmouseover等以on开头的属性必须被剥离 - 是否限制
href和src的协议白名单?仅允许http://、https://、data:image/,拒绝javascript:、vbscript:、data:text/html, -
style属性是否受限?expression()、url(javascript:...)等 CSS 注入手法需被识别并清除
输出上下文决定编码方式,不能统一用 HTML 实体转义
同一个用户输入,在不同位置需要不同处理:插入到 HTML 文本节点、作为属性值、嵌入到 JavaScript 字符串、写入 URL 参数,安全要求完全不同。
常见错误:
- 把所有输出都套用
escapeHtml(),但当用户输入被插进<script>var name = "${name}";</script>时,仅 HTML 转义无法阻止闭合引号后执行任意 JS - 在 URL query 中直接拼接未编码的用户输入,导致
location.href = '/search?q=' + userInput被注入q=xxx%22%3E%3Cscript%3E... - JSON 输出未使用
JSON.stringify()而是手动拼字符串,遗漏对\u2028、\u2029的处理,引发 JS 解析中断或注入
第三方依赖是否引入未经净化的 HTML 渲染逻辑
很多项目依赖 marked、highlight.js、mermaid 等库渲染富文本,它们默认不开启 XSS 防护,或需显式配置 sanitizer。
实操建议:
- 检查
marked是否启用sanitize: true(v4+ 已弃用,改用renderer+ 自定义 sanitize) -
highlight.js渲染代码块本身安全,但若配合dangerouslySetInnerHTML(React)或v-html(Vue)直接插入结果,需确认其输出不含可执行内容 - 任何支持 LaTeX、流程图、数学公式的扩展(如 KaTeX、Mermaid),都必须验证其 parser 是否对用户输入做沙箱隔离或语法树级清洗
真正难的不是写一个过滤函数,而是让每个开发成员理解:HTML 安全不是“有没有过滤”,而是“在哪一层、用什么规则、针对哪一类上下文做过滤”。漏掉任意一环,攻击者就能找到那条没关严的缝。



















