You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何检测水印DOM元素的变更?含开发者工具修改场景

检测水印DOM变更与样式篡改的解决方案

针对你遇到的水印检测需求,以下是几个可行的技术方案,覆盖DOM移除、样式属性修改(包括开发者工具操作)的场景:

一、正确配置MutationObserver(核心方案)

你之前提到MutationObserver只能检测代码修改是配置不当导致的——它完全可以监听开发者工具对DOM和样式的修改。关键要同时监听元素移除和样式属性变化:

具体实现步骤

  1. 监听水印元素的父节点,捕获水印被移除/替换的事件
  2. 监听水印元素自身的style属性变更,同时可扩展监听class属性(防止通过类名修改样式)
  3. 通过getComputedStyle获取实际生效的样式,判断是否被篡改
const watermark = document.getElementById('watermark');
const parentNode = watermark.parentNode;

// 配置监听规则:监听样式属性、类名变化,以及父节点的子元素增删
const elementObserverConfig = {
  attributes: true,
  attributeFilter: ['style', 'class'], // 只监听目标属性,提升性能
  attributeOldValue: true // 保留旧值,方便对比变更内容
};

const parentObserverConfig = {
  childList: true,
  subtree: false // 仅监听父节点直接子元素,无需深入子树
};

const observer = new MutationObserver((mutations) => {
  mutations.forEach(mutation => {
    // 处理水印被移除的情况
    if (mutation.type === 'childList') {
      if (!document.contains(watermark)) {
        console.log('水印已被从DOM中移除');
        // 这里执行恢复逻辑,比如重新创建并插入水印
        restoreWatermark();
      }
    }
    // 处理样式/类名篡改
    else if (mutation.type === 'attributes') {
      const computedStyle = window.getComputedStyle(watermark);
      const isHidden = computedStyle.opacity === '0' 
        || computedStyle.display === 'none' 
        || computedStyle.visibility === 'hidden';
      
      if (isHidden) {
        console.log('水印样式被篡改,已隐藏');
        // 强制恢复水印样式
        Object.assign(watermark.style, {
          opacity: '1',
          display: 'block',
          visibility: 'visible'
        });
      }
    }
  });
});

// 启动监听:同时监听父节点(检测移除)和水印元素(检测样式)
observer.observe(parentNode, parentObserverConfig);
observer.observe(watermark, elementObserverConfig);

// 示例:恢复水印的函数
function restoreWatermark() {
  const newWatermark = document.createElement('div');
  newWatermark.id = 'watermark';
  // 填充水印的样式和内容
  newWatermark.style.cssText = 'position:absolute; top:0; left:0; ...';
  newWatermark.textContent = '版权所有';
  parentNode.appendChild(newWatermark);
  // 重新绑定监听
  observer.observe(newWatermark, elementObserverConfig);
}

二、结合WeakRef与setInterval做兜底检测

如果担心MutationObserver在某些极端场景下失效(比如开发者工具快速删除并替换元素),可以用WeakRef避免内存泄漏,配合定时检测做兜底:

const watermarkRef = new WeakRef(document.getElementById('watermark'));
const checkInterval = setInterval(() => {
  const watermark = watermarkRef.deref();
  if (!watermark) {
    console.log('水印已被移除');
    clearInterval(checkInterval);
    restoreWatermark();
    return;
  }

  const computedStyle = window.getComputedStyle(watermark);
  const isHidden = computedStyle.opacity === '0' 
    || computedStyle.display === 'none' 
    || computedStyle.visibility === 'hidden';
  
  if (isHidden) {
    console.log('水印样式被篡改');
    Object.assign(watermark.style, {
      opacity: '1',
      display: 'block',
      visibility: 'visible'
    });
  }
}, 500); // 检测间隔可根据需求调整,建议500-1000ms

这里WeakRef的作用是:当水印元素被DOM移除后,不会阻止浏览器垃圾回收该元素,避免内存泄漏。

三、额外防御:监听祖先元素样式(可选)

如果攻击者通过修改水印的父元素样式来隐藏水印(比如给父元素加display:none),可以扩展监听父元素甚至更高层级祖先的样式变化,或者在检测时遍历祖先元素的计算样式:

// 示例:检查所有祖先元素是否被隐藏
function isAncestorHidden(element) {
  let current = element.parentNode;
  while (current && current !== document.body) {
    const style = window.getComputedStyle(current);
    if (style.display === 'none' || style.visibility === 'hidden') {
      return true;
    }
    current = current.parentNode;
  }
  return false;
}

// 在MutationObserver或setInterval中调用
if (isAncestorHidden(watermark)) {
  console.log('水印被祖先元素隐藏');
  // 恢复祖先元素样式或调整水印层级
}

注意事项

  • 无法完全阻止技术人员篡改:开发者工具完全可控,上述方案只能增加篡改成本,无法做到100%防御
  • 性能平衡:MutationObserver是异步触发,性能优于setInterval;定时检测的间隔不要太小,避免影响页面性能
  • 恢复逻辑要健壮:重新创建水印时,要确保不会重复添加,且样式、层级与原水印一致

内容的提问来源于stack exchange,提问作者prettyInPink

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 10:45:04