真正有效的白名单校验必须结合结构化解析与语义判断:先用 DOMParser 提取 body 内容并清洗注释、script、style 等,再逐节点校验标签名、属性名及敏感属性值,服务端须二次校验。

直接用 String.matches() 检测 DOM 模板片段是否合规,不可靠也不安全——它只做正则全文匹配,无法识别嵌套结构、混淆写法或上下文语义,容易漏掉 <img onerror=alert(1)> 这类危险内联事件,也难以区分合法注释与伪装脚本。真正有效的白名单校验,必须结合结构化解析与语义判断。
先提取纯模板内容,剥离干扰
用户可能上传带 HTML 文档头、注释、CDATA 或多层包裹的字符串(如 <!DOCTYPE html><body><div>...</div></body>),而你真正要校验的是其中可渲染的 DOM 片段。应先做最小化清洗:
- 用正则或
DOMParser提取<body>内容或根级子节点(如div,section) - 移除所有注释节点(
<!-- ... -->)、<script>和<style>标签及其内容 - 拒绝含
<html>,<head>,<title>等文档级标签的输入——这些不属于“模板片段”范畴
用 DOMParser + 遍历做结构化白名单检查
matches() 是字符串级操作,而 DOM 安全校验是树级操作。正确做法是将字符串解析为真实 DOM 节点,再逐层检查:
- 创建
new DOMParser().parseFromString(htmlStr, 'text/html') - 遍历所有元素节点,确认其 tagName 在白名单中(如
['div', 'span', 'p', 'ul', 'li', 'a']) - 对每个元素检查属性:仅允许
class,id,data-*,href(限于a)、src(限于img)等安全属性 - 禁止任何以
on开头的属性(onclick,onload)、javascript:协议、data:text/html类型的src
对关键属性值做精细化校验(非仅 matches)
即使标签和属性名合法,值仍可能藏风险。例如 <a href="javascript:alert(1)"> 中 href 名字合法,但值非法:
- 对
href、src、style等敏感属性,不依赖matches(),而用专用逻辑校验 -
href值只接受空值、#、以https?://或/开头的相对路径 -
style属性需用 CSS 解析器(如CSSStyleSheet.insertRule尝试注入)或白名单属性值对(如color:red,font-size:14px)校验 - 避免用
.matches("javascript:.*")这类简单正则——它会被javascript:alert(1)绕过
补充:服务端必须二次校验
前端 DOM 校验可提升体验,但不能替代服务端防护。用户禁用 JS、绕过解析逻辑、或直接调用 API 时,服务端仍需:
- 重新用服务端 DOM 解析器(如 Java 的 Jsoup、Node 的 jsdom、Python 的 BeautifulSoup)执行同样白名单规则
- 检测是否存在未闭合标签、自闭合错误、编码混淆(如 Unicode 替换、HTML 实体绕过)
- 对最终渲染结果添加
sandboxiframe 包裹或 CSP 头限制执行能力

















