html本身不能脱敏,真正脱敏必须在服务端渲染时或前端js插入dom前完成;服务端模板层脱敏最可靠,前端js脱敏仅适用于低敏场景,动态权限型脱敏必须由后端控制。

HTML 本身不能做脱敏,所有试图在纯 HTML 标签里“写死”脱敏逻辑的方案都无效。真正起作用的脱敏必须发生在服务端渲染时、或前端 JS 插入 DOM 前的那一刻。
服务端模板层脱敏(最可靠)
这是唯一满足金融、政务等强合规场景的方式:敏感字段在后端组装 HTML 或 JSON 时就已变形,浏览器拿到的就是 "138****1234" 这样的字符串,不存在“先明文再遮盖”的风险。
- Spring Boot + Thymeleaf:用
@符号调用工具类方法,如${#strings.substring(phone,0,3) + '****' + #strings.substring(phone,-2)} - MyBatis XML 中用
<bind></bind>预处理:例如<bind name="maskedPhone" value="phone.substring(0,3) + '****' + phone.substring(phone.length()-2)"></bind> - Node.js 模板(EJS / Pug):直接在
里写phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2'),但注意仅限服务端执行,不能把该逻辑暴露给前端
前端 JS 脱敏(仅限低敏、内部系统)
必须在数据写入 innerHTML 或 textContent 之前完成,否则用户会看到明文闪现;且不能依赖 querySelectorAll 后遍历 DOM 补救——那已经晚了。
- React/Vue 场景:在组件 render 前对 props 数据做处理,例如
maskPhone(user.phone),而不是在useEffect里改 DOM - 原生 JS 场景:从接口取回数据后立刻处理,再塞进
document.getElementById('phone').textContent = maskPhone(data.phone) - 常见错误:用
btoa()编码手机号,结果控制台一查atob("MTM4KioqKjEyMzQ=")立刻还原;这根本不是脱敏,只是可逆编码
千万别碰的“伪脱敏”陷阱
这些做法看似有动作,实则毫无防护能力,还会制造虚假安全感。
- 给
<td> 加自定义属性如 <code>data-encrypted="true",不改变实际文本内容 - 用 CSS
text-security: disc或伪元素遮挡,右键“检查元素”仍能看见原始 HTML 字符串 - 把 AES 密钥硬编码在 JS 文件里,打包后静态提取密钥 + 密文,5 分钟内就能解出身份证号
- 在
localStorage存“已脱敏”的手机号,但攻击者只要打开控制台执行localStorage.getItem('phone')就拿走了 - 正确做法:请求头带
Authorization和角色标识,后端根据 session 或 token 解析真实权限,再决定返回{"phone": "13812345678"}还是{"phone": "138****5678"} - 导出 Excel/PDF 时同样要走同一套后端脱敏逻辑,不能前端 JS 拼 HTML 表格再转 CSV
- 特别注意:URL 参数、
<meta>标签、data-属性、注释里都不能出现未脱敏的敏感字段,支付宝等平台会直接拦截接口
动态权限型脱敏必须由后端控制
如果 A 角色能看到完整手机号、B 角色只能看 "138****1234",这个判断绝对不能放在前端 JS 里,比如 if (role === 'admin') { showRaw() } —— role 字段可被任意篡改。
最容易被忽略的一点:脱敏不是“显示时藏起来”,而是“传输和存储时就不该有明文”。哪怕只在 JS 变量里存了一次 const rawIdCard = '11010119900307271X',就已经违反基本安全原则。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











