如何作恶(但请不要!)– modal 和 overlay 版

Avatar of Ana Tudor
Ana Tudor

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

我们都遇到过这种情况。访问一个网站,却突然弹出一个 modal,看起来就像下面这样

Screenshot of a modal saying: Hey there, if you wish to see this content, give us money (followed by two buttons for full and limited access) or at least give us your data so we can make money off you somehow (followed by an input field for an email and a
你好,黑暗,我的老朋友。

对我来说,这会引发一种本能反应:诅咒他们获得了一个页面浏览量,关闭标签页,永不再访问。但也有可能我们确实想获取 modal 后面的信息。 因此,在这种情况下,下一步就是打开开发者工具,删除 modal 和 overlay,顺便可能还会删除一些其他使页面混乱的无用东西。

从这里开始,这个页面就变得有点邪恶了。

我们可能无法通过“检查元素”访问开发者工具,因为上下文菜单可能被禁用。这个很容易,他们只需要一行代码

addEventListener('contextmenu', e => e.preventDefault(), false);

但是,没关系,我们可以使用键盘快捷键,对吧?没错……除非有一段类似于下面代码的 JavaScript 正在运行

addEventListener('keydown', e => {
  if(e.keyCode === 123 /* F12: Chrome, Edge dev tools */ || 
    (e.shiftKey && e.ctrlKey && (
     e.keyCode === 73 /* + I: Chrome, FF dev tools */ || 
     e.keyCode === 67 /* + C: Chrome, FF inspect el */ || 
     e.keyCode === 74 /* + J: Chrome, FF console */ || 
     e.keyCode === 75 /* + K: FF web console */ || 
     e.keyCode === 83 /* + S: FF debugger */ || 
     e.keyCode === 69 /* + E: FF network */ || 
     e.keyCode === 77 /* + M: FF responsive design mode */)) ||
    (e.shiftKey && (
     e.keyCode === 118 /* + F5: Firefox style editor */ || 
     e.keyCode === 116 /* + F5: Firefox performance */)) ||
    (e.ctrlKey && e.keyCode === 85 /* + U: Chrome, FF view source */)) {
    e.preventDefault();
  }
}, false);

尽管如此,浏览器菜单还是无法操作的。这最终会为我们打开开发者工具!万岁!删除那些可怕的节点,然后……看到页面刷新了,因为有一个 mutation observer 正在监视此类操作。就像下面这段代码一样

addEventListener('DOMContentLoaded', e => {
  let observer = new MutationObserver((records) => {
    let reload = false;
    
    records.forEach((record) => {
      record.removedNodes.forEach((node) => {
        if(node.id === 'overlay' && !validCustomer())
          reload = true;
      });

      if(reload)
        window.location.reload(true);
    });
  });
	
  observer.observe(
    document.documentElement, {
      attributes: true,
      childList: true,
      subtree: true,
      attributeOldValue: true, 
      characterData: true
    }
  );
});

“这是战争!”模式启动!好吧,那就修改 modal 和 overlay 的样式!但是,所有样式都是内联的,并且在顶部使用了!important,因此如果没有修改style属性,我们就无法覆盖所有样式。

Screenshot of developer tools showing elements with a lot of inline styles with !important on the tail of the values.
哦,!important 的重要性……

有些人可能还记得 256 个类如何覆盖一个 id,但与此同时,WebKit 浏览器已经改变了这一点(不过在 Edge 和 Firefox 中仍然存在),并且256 个 ID 从未能够覆盖内联样式

好吧,那就修改style属性。但是,另一个“惊喜”在等着我们:还有一段 JavaScript 正在监视属性更改

if(record.type === 'attributes' && 
   (record.target.matches && 
    record.target.matches('body, #content, #overlay')) && 
   record.attributeName === 'style' && !validCustomer())
  reload = true;

因此,除非有一些样式可以帮助我们移除 overlay,而编写这个糟糕代码的人没有设置内联样式,否则我们无法通过修改style属性来解决这个问题。

首先要查找的是display属性,因为将其在 overlay 上设置为none可以解决所有问题。然后是positionz-index的组合。即使这些属性在实际的 overlay 上是内联设置的,如果它们没有在 overlay 下方的实际内容上内联设置,那么我们可以选择为内容设置更高的z-index值规则,并将其置于 overlay 之上。但是,不太可能有人会错过设置这些属性。

如果偏移量(toprightbottomleft)不是内联设置的,它们可以帮助我们将 overlay 移出页面,margintranslate属性也是如此。类似地,即使它们在 overlay 上是内联设置的,但没有在实际内容上和 overlay 和内容的父元素上内联设置,那么我们可以通过类似于100vw的东西横向移动该父元素,然后将内容移回视图中。

归根结底,即使是opacitypointer-events的组合也可以奏效。

但是,如果编写恼人 overlay 的人没有错过设置任何这些内联样式,并且我们有那段 JS 代码……我们无法修改内联样式而不导致页面重新加载。

但是等等!有一些东西可以使用!important覆盖内联值,很多人都可能忽略了它……那就是@keyframe动画!在新的或已存在的style元素中添加以下 CSS 代码,可以将 overlay 和 modal 置于实际内容之后

#overlay { animation: a 1s infinite }

@keyframes a { { 0%, to { z-index: -1 } } }

但是,可能有一段 JavaScript 阻止我们添加新的stylelink元素(以引用外部样式表)或修改已存在的元素以包含上述 CSS 代码,如此处所示。

if(record.type === 'characterData' && 
   record.target.parentNode.tagName.toLowerCase() === 'style')
    reload = true;			
record.addedNodes.forEach((node) => {
  if(node.matches && 
     node.matches('style:not([data-href]), link[rel="stylesheet"]'))
      reload = true;
});

查看 Ana Tudor 在 CodePen 上创建的 Pen 如何作恶(但请不要)@thebabydino)。

但是,如果已经存在一个外部样式表,我们可以在其中添加我们的 CSS 代码,并且除了在requestAnimationFrame循环中检查更改之外,没有办法检测到此类更改(有人谈论过样式变异观察器,但目前我们还没有任何此类功能)。

当然,animation属性也可以内联设置为none,这将阻止我们使用此解决方法。

但在这种情况下,我们仍然可以禁用 JavaScript 并删除 overlay 和 modal。或者只需通过浏览器菜单查看页面源代码。底线是:如果内容已经存在,位于 overlay 后面,那么绝对没有办法阻止访问它。但尝试这样做肯定会让人诅咒你。

我写这篇文章是因为我最近确实遇到了类似的事情。我最终获得了内容。只是花费了更多时间,并且让我讨厌编写该代码的人。

有人可能会争辩说,这些 overlay 在阻止非技术人员方面非常有效,非技术人员占网站访问者的绝大多数,但当他们费尽心思地阻止打开开发者工具、删除节点、更改样式等,并将执行此操作的整个 JavaScript 代码打包成十六进制时,他们试图阻止的不仅仅是非技术人员。