mutationobserver 必须精准监听目标元素而非 document.body,配置 attributefilter 和 attributeoldvalue,回调中执行四重校验,修复时先 disconnect 再单属性恢复并重新 observe。

MutationObserver 必须只监听目标元素,不能监听 document.body
监听整个 document.body 会导致每帧广告注入、统计脚本加 class、轮播图切换都触发回调——不是漏报,而是根本无法区分真实篡改。真实防护必须把观察范围缩到最小:直接选你要保护的节点,比如 document.querySelector('#submit-btn') 或带语义标识的容器 document.querySelector('[data-protected="payment"]')。一旦用 observer.observe(document.body, {...}),等于主动放弃精度,后续所有逻辑都会被噪声淹没。
attributes 配置要配合 attributeFilter 和 attributeOldValue
只开 attributes: true 不够,它会监听所有属性变更,包括第三方库自动加的 data-reactroot、aria-hidden 等无关字段。必须显式指定 attributeFilter: ['class', 'style', 'disabled', 'hidden', 'data-*'](按需增减),并始终启用 attributeOldValue: true。否则你拿到新值却不知道旧值是什么,无法判断是正常更新还是恶意覆盖——比如 disabled 从 "" 变成 null,或 class 末尾被悄悄拼上 tampered,没旧值比对就只能猜。
回调里必须做四重校验,缺一不可
收到 mutation 后别急着修复,先快速验证是否真被篡改:
-
mutation.target === target—— 排除子元素触发(如按钮内 span 被改) -
mutation.type === 'attributes'—— 过滤掉 characterData 或 childList 类变动 -
attributeName在你声明的attributeFilter列表里 -
target.getAttribute(mutation.attributeName) !== mutation.oldValue—— 确认值确实变了,而非重复设置相同值
少一步就可能把框架内部重渲染当成攻击,频繁 disconnect/reobserve 会拖慢主线程。
修复动作必须先 disconnect,再单属性恢复,最后重新 observe
修复过程本身会再次触发 mutation,不控制就会死循环。正确顺序是:
- 调用
observer.disconnect() - 仅恢复被篡改的单个属性:
target.setAttribute('class', 'btn primary'),绝不碰innerHTML或批量重写 - 再调用
observer.observe(target, config)恢复监听
这个流程看似多一步,但能避免 DOM 重建开销和事件监听器丢失。尤其要注意:如果目标元素被移出父级再 append 回来,它的事件监听器可能失效,此时仅靠 MutationObserver 无法兜底——它只管属性,不管绑定关系。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











