HTML导出时敏感信息脱敏必须在服务端模板层或前端数据注入DOM前完成,而非导出后用正则清理或DOM操作覆盖,否则明文已暴露,不满足合规要求。

导出 HTML 时批量删除(实为脱敏)敏感信息,不是“删掉字段”,而是把手机号、身份证、邮箱等字段在写入 HTML 前就变形为 138****1234 这类格式;真删字段会破坏数据结构,且服务端返回的原始数据仍存在泄露风险。
为什么不能用正则在导出后“清理HTML”?
常见错误是导出完整 HTML 文件,再用 innerHTML.replace(/]*>/g, "") 或在线工具去标签——这根本没动敏感文本,只是剥了标签。更危险的是:用 JS 在浏览器里跑 document.querySelectorAll('td.phone')?.forEach(el => el.textContent = maskPhone(el.textContent)),此时明文已在 DOM 中渲染过,开发者工具一眼可见,截图、录屏、XSS 都能直接捕获。
- HTML 导出动作通常发生在服务端(如 PDF 渲染、模板生成)或前端序列化前,脱敏必须卡在这个时间点
- 若导出的是静态 HTML 文件(如报表快照),脱敏必须在生成该文件的代码里完成,而非事后修补
- 用
textContent或innerText覆盖 DOM 是补救,不是防护;合规审计不认这种“马后炮”
服务端模板层脱敏才是可靠方案
所有强监管场景(金融、政务、医疗)都要求敏感字段在服务端组装 HTML 时就完成脱敏,浏览器拿到的就是掩码结果,无明文可挖。
- Spring Boot + Thymeleaf:
${#strings.substring(user.phone,0,3) + '****' + #strings.substring(user.phone,-2)} - Node.js(EJS):
(注意:仅限服务端执行,不可暴露给前端 JS) - PHP(Twig):
{{ user.idCard|replace({'/^(\d{6})\d{8}(\d{4})$/': '$1********$2'}) }} - MyBatis XML 中预处理:
前端 JS 脱敏只适用于低敏内部系统
如果导出逻辑完全在前端(例如用 html2canvas 截表格、或拼接 data:text/html 下载),那脱敏必须在数据注入 HTML 字符串之前完成,且不能依赖 DOM 查询。
- 错误写法:
el.innerHTML = dataRow; document.querySelector('.phone').textContent = mask(dataRow.phone)→ 明文已进 DOM - 正确写法:先处理数据,再拼 HTML:
const masked = {...dataRow, phone: maskPhone(dataRow.phone)}; const html = ` `${masked.phone} ... - 避免伪脱敏:
btoa()、CSS text-security、data-* 属性存原始值全部无效,检查元素即暴露 - 若用 React/Vue,脱敏逻辑应放在
map渲染前或自定义 Hook 中,而非useEffect操作 DOM
导出为文件时的额外陷阱
当用户点击“导出 HTML”生成本地文件,很多人忽略两个关键点:文件名和编码声明可能暴露上下文,且未转义的 ID 可能引入 XSS。
- 文件名别含敏感词:
export-user-张三-20261027.html→ 改为report_20261027_8a3f.html(用哈希或随机 ID) - HTML 中所有动态插入的值(包括
value、data-id、title)必须做 HTML 实体转义,防止<script></script>注入 - 不要在导出 HTML 中保留
console.log、debugger或未清理的注释,它们可能泄露字段名或业务逻辑 - 若导出内容含图表或图片,确认其 URL 不携带
token=xxx或用户 ID 参数
真正难的不是写一个 maskPhone 函数,而是确保它在数据离开服务端前、或注入 DOM 前那一刻被调用;任何发生在浏览器渲染之后的操作,都只是给审计报告加一行“存在明文残留风险”的备注。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











