前端无法真正阻止恶意dom修改,只能事后检测修复;需精准定位监听节点、配置attributefilter和attributeoldvalue、执行四重校验、断开后再修复,并依赖服务端签名校验等后端防线。

靠前端代码本身无法真正阻止恶意 DOM 修改;所有客户端监听和修复逻辑,都只是在篡改发生后做响应,且极易被绕过或拖垮性能。真正在意防护效果,必须从目标范围、监听粒度、校验逻辑三处下刀。
MutationObserver 必须只监听具体业务节点,不能监听 document.body
监听 document.body 会导致每帧广告注入、客服浮窗 class 切换、A/B 测试脚本加 data-variant 都触发回调——不是漏报,而是根本分不清哪次是真实攻击。
- 精准定位:用
document.querySelector('#pay-button')或带语义的document.querySelector('[data-protected="price"]') - 异步加载容错:若目标节点由 React/Vue 动态挂载,加
requestIdleCallback延迟初始化,避免querySelector返回null - 挂载校验:回调中先判断
if (!target.isConnected),排除 document fragment 中未挂载的临时节点
attributes 配置必须搭配 attributeFilter 和 attributeOldValue: true
只开 attributes: true 会监听所有属性变更,包括 data-reactroot、aria-hidden 等框架自动生成字段,徒增 30%+ 回调开销,且无法判断是否真被改。
- 显式声明:如
attributeFilter: ['class', 'style', 'disabled', 'hidden', 'data-locked'] - 旧值必开:始终设
attributeOldValue: true,否则mutation.oldValue为undefined,无法比对class是否被拼接了tampered - 避开
data-*全匹配:若业务中data-id频繁更新,就不要写'data-*',单独列明需监控的 key
回调里必须四重校验,缺一不可
收到 mutation 后别急着修复,否则框架 diff、用户操作、甚至你自己刚插进去的节点都会被误判为攻击,频繁 disconnect/observe 会卡主线程。
-
mutation.target === target:排除子元素触发(如按钮内span被改) -
mutation.type === 'attributes':过滤掉characterData或childList类变动 -
mutation.attributeName在attributeFilter列表里 -
target.getAttribute(mutation.attributeName) !== mutation.oldValue:确认值确实变了,而非重复设置相同值
修复前必须 observer.disconnect(),且禁用 innerHTML = ''
不 disconnect 就直接修改 DOM,极易触发死循环:修复动作本身又产生新 mutation,再次进回调,无限递归。
- 修复流程固定为:断开 → 单属性还原(如
target.setAttribute('class', oldValue))→ 重连 - 禁用
innerHTML = ''清空:它会销毁全部子节点并重建,触发大量childList变更,干扰后续监听 - 改用
node.remove()或node.replaceWith(newNode),最小化 DOM 变动幅度
最易被忽略的是:所有这些机制都建立在“篡改已发生”的前提上,它不阻止,只检测与恢复;而真正的防线,从来不在 JS 里——是服务端对关键字段的 HMAC 签名校验、是 WAF 对 /index.html 和关联 main.js 的实时比对、是静态资源托管于只读对象存储。前端防篡改,本质是一道延迟暴露时间的缓冲带,不是城墙。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











