mutationobserver 应精简配置并精准监听关键节点,开启childlist、subtree、attributes(限class/style/hidden)和characterdata,禁用旧值比对;回调中需过滤目标区域变更并安全修复。

MutationObserver 的异步监听不是“等变化再处理”,而是把 DOM 变更攒成一批,等当前 JS 执行栈清空后,用微任务统一派发——这既避开主线程阻塞,又天然适配 Vue/React 的更新节奏。
只盯要害区域,不扫全页
监听 document.body 会淹没在广告、埋点、框架 diff 的噪音里。真正要防的篡改集中在几个节点:
- 登录表单容器(如 #login-form 或 [data-role="auth-section"])
- 支付按钮与金额区(如 .price-display、#pay-button)
- 用户协议原文块(如 [data-content="terms"])
- 法律声明区域(如 #legal-notice)
用 document.querySelector 精准获取目标节点再传给 observer,比依赖动态 class 更稳。
配置精简但覆盖高频篡改
95% 的恶意操作逃不出三类:加节点、删节点、改样式或文本。对应开这些选项就够了:
- childList: true —— 必开,捕获伪造按钮、广告 div、注入 script
- subtree: true —— 必开,防止深层嵌套篡改(比如在 #pay-button 内部插 iframe)
- attributes: true + attributeFilter: ['class', 'style', 'hidden'] —— 防 display:none、visibility:hidden、伪造 data-hook
- characterData: true —— 法律文本必须开,防静默替换“版权所有”等关键字
- 禁用 attributeOldValue 和 characterDataOldValue —— 不比对旧值,省下 30%+ 回调开销
回调里做轻量可信判定
每次回调收到的是批量记录,混着你自己的操作、框架更新甚至修复动作。不筛就动,容易误伤或死循环:
- 先用 container.contains(mutation.target) 确认变更发生在目标区域内
- type 是 'childList'?遍历 addedNodes,匹配 script[src*="malicious"]、[id^="ad-"]、iframe[src*="zopim"]
- type 是 'attributes' 且 attributeName === 'style'?检查是否含 display: none 或 visibility: hidden
- 有 removedNodes?确认是否删掉了已知关键节点(如 #real-pay-btn)
修复动作要安全可控
一旦判为恶意,修复不能简单粗暴:
- 移除可疑节点时,用 node.remove() 而非 innerHTML 清空,避免触发二次解析
- 恢复被隐藏元素,优先设 style.display = '',而非直接删 style 属性(保留其他合法样式)
- 所有修复操作后,加一次 observer.takeRecords() 清空待处理队列,防重复响应
- 必要时配合 CSP 和子资源完整性校验,构成纵深防线











