返回概览 更多文章

网站如何检测你的广告拦截器(以及它们何时误判)

正常
正在检测 AdBlock…

正在检测你是否安装了广告拦截器…

你点开一篇文章,迎面撞上的却是一堵墙:“检测到广告拦截器,请停用后继续访问。“也许这个指控没冤枉你;也许你压根没装广告拦截器,网站却咬定你装了。但无论哪种情况,网站都从未见过你的扩展列表——它照样把你抓住了。

没有任何 API 会把你的扩展列表交给网页。所以反广告拦截脚本找的从来不是拦截器本身:它们会在页面里埋下一些拦截器必然会清除的东西,再回头查看哪些活了下来。本页上的小工具此刻正对你的浏览器运行其中六个陷阱;本文接下来会逐一解释它们各自在做什么。

简而言之:网页没法查询你装了哪些扩展,于是改为设下陷阱:一个假广告元素,外加几条假广告请求。诱饵消失了,就说明你在拦截。整套把戏就是这样,这也是为什么一些根本不是广告拦截器的东西(DNS 过滤、Brave 的 Shields、某些杀毒软件)也会跟着被冤枉。陷阱检测的是"拦截"这件事,而不是拦截器。

这背后的经济账很简单:广告养活页面,被拦截的广告一分钱也带不来,而一堵能把一部分拦截广告的访客重新变回广告展示的墙,哪怕惹恼其余的人也是划算的。下文这些脚本存在的全部理由,就在这里。

诱饵元素:因”消失”而暴露

几乎每一款广告拦截器都内置同一套核心规则:EasyList,一份由社区维护、描述”广告在页面上长什么样”的清单。除了数以万计的 URL 匹配模式,它还带有外观(cosmetic)规则:隐藏所有带 ad-unitadsbox 之类 class 名的元素。

这份清单是公开的,反广告拦截脚本自然也会去读。检测手段里最古老的一招,就是创建一个不可见的一像素 div,给它恰好套上这些 class 名,然后读取它的 offsetHeight。没有拦截的浏览器会报告这一个像素;被拦截的浏览器报告的是零,因为某条外观过滤规则把这个 div 设成了 display: none。页面从头到尾都没见过你的扩展,它看到的只是一个 div 不复存在。

一个诱饵 div,两种结局
页面埋下的东西
<div class="adsbox ad-unit banner_ad">
被外观过滤规则隐藏
检测器读到的结果
offsetHeight: 0
诱饵消失了。检测到广告拦截器。
检测器埋下一个已知会被广告拦截器隐藏的元素,量一量它,再从它留下的空洞里读出你的拦截器。

这就是上方小工具里的 “CSS 诱饵” 检查,也是老牌的 BlockAdBlock 和 FuckAdBlock 库的核心引擎——互联网上半数 “检测 adblock” 教程至今仍在重新打包它。这种旁敲侧击的思路,和反机器人系统追猎浏览器自动化时的招数如出一辙:谁也看不见 Puppeteer,能看见的只有 Puppeteer 改变的东西。

一去不回的请求

第二类陷阱布在网络层。拦截器不只是隐藏元素,还会拒绝向已知的广告服务器发起请求,于是页面主动放出诱饵:从 pagead2.googlesyndication.com 加载一个脚本,从 tpc.googlesyndication.com 加载一张图片,再向 ads.pubmatic.com 发一个 fetch()。这些都是货真价实的广告基础设施,是页面故意去请求的。如果触发的是错误回调而不是加载事件,那就说明你机器上的某样东西掐断了这条请求。Chrome 的控制台甚至会直接点出凶手的名字:net::ERR_BLOCKED_BY_CLIENT

小工具的六项检查里有四项都是这个形态,每种请求类型各占一项——因为过滤规则对脚本、图片、CSS 加载的图片和 fetch 请求的处理各不相同,一款拦截器完全可能在这一项上被抓、在另一项上安然通过。

不少网站干脆连诱饵都省了。Google 自家的广告脚本 adsbygoogle.js 在加载时会定义一个 JavaScript 对象,页面只需检查这个对象是否存在即可。当真正的广告脚本本身就是诱饵时,“拦截它”和”暴露自己”就成了同一件事。

YouTube 如何检测你的广告拦截器(以及为什么它是另一回事)

YouTube 根本不屑于用诱饵 div。它的播放器清楚服务器给视频流挂上了哪些广告,预期自己的播放事件会依次触发,而检测逻辑就藏在播放器的 JavaScript 里,经过混淆并且频繁重排,逼得过滤列表的维护者只能不停打补丁跟进。Google 还试验过服务端广告插入,把广告直接缝进视频流里,任何过滤列表都够不着。这和一段到处复制粘贴的诱饵脚本完全不在同一个重量级,背后还有政策撑腰:Google 的立场是广告拦截器违反 YouTube 的服务条款。那里的军备竞赛节奏快、针对性强,这正是 “YouTube 是怎么检测 adblock 的” 这个问题没有稳定答案的原因。而本页讲的诱饵机制有稳定答案——其余百分之九十九的”请停用您的广告拦截器”弹墙,跑的都是它。

没装广告拦截器也被指控

在 Google 里输入 “adblock detected”,排名靠前的联想词之一就是 “adblock detected but no adblock”(检测到 adblock 但我没装)。这些陷阱恰好解释了这种困惑:只要存在拦截,无论它藏在哪一层,陷阱都会触发。

  • DNS 过滤。 Pi-hole、NextDNS,或是自带广告过滤的路由器,会在诱饵请求离开你的网络之前就把它掐灭。在诱饵看来,这和 uBlock Origin 没有任何区别。
  • 默认就拦截的浏览器。 Brave 的 Shields 出厂默认就会丢弃广告请求,Firefox 的严格跟踪保护也会拦截已知的广告和跟踪域名。全程不需要任何扩展。
  • 其他扩展。 Cookie 横幅清除器和各类隐私工具会出于自己的目的隐藏元素、拦截请求,结果踩响的是同样的陷阱。
  • 安全软件。 带”网页防护”功能的杀毒套件会替整台机器过滤广告域名。

检测器分不清是哪一层吞掉了它的诱饵,而那块弹墙也根本不关心。如果你正处于这种境地,上方的小工具可以帮你确诊:它会指出六项检查里究竟是哪几项触发了。元素检查触发,说明有东西在隐藏页面内容;请求检查触发,则说明问题出在网络路径上的某个环节。

如何不让网站检测到你的广告拦截器

你有三个体面的选项,外加一对简单粗暴的。

  • 在拦截器里把该站点加入白名单(允许列表)。 这正是那堵墙存在的目的所在,也是 Adblock Plus 借默认开启的 Acceptable Ads 计划固定下来的机制。
  • 让过滤列表保持最新。 拦截广告的这套生态同样在拦截反广告拦截:EasyList 专门维护着一份 Adblock Warning Removal List,uBlock Origin 也内置了多份 Annoyances 列表,能在许多弹墙出现在你眼前之前就把它们剥掉。
  • 为你真正阅读的网站付费。 直接终结这场对峙。
  • 阅读模式,或者干脆关掉 JavaScript。 这就是那对粗暴的选项:它们能干掉检测器,但页面赖以运转的其他一切也会陪葬。

拦截广告是合法的,网站在你停止拦截之前拒绝提供服务也同样合法。德国最高法院在 2018 年就给出了这个结论——当年出版商起诉 Adblock Plus 的开发商,结果败诉。既然法律在两边都已尘埃落定,剩下的就只有军备竞赛了。

你的拦截器成了你指纹的一部分

在我们的五款热门广告拦截器横评中,我们用同样这六项检查逐一测了每款拦截器,结果差距悬殊:Adblock Plus 六项中了五项,uBlock Origin 中了两项,Ghostery 只中了一项。

五款拦截器对阵六个陷阱
广告拦截器主检测CSS 诱饵脚本图片CSS 背景Fetch中招数
Ghostery1/6
uBlock Origin2/6
uBlock Lite2/6
AdBlock3/6
Adblock Plus5/6
该检查能测出这款拦截器 拦截器成功溜过
同一个页面,同样六个陷阱:Adblock Plus 踩响了五个,Ghostery 只踩响一个。这一行的形状和分数一样,都足以识别出一套配置。

由此可以得出两点。第一,拦截器的”响动”并不一样大,“可检测性”是一项挑选时就能货比三家的属性。第二,这个触发模式本身就是信息。哪些陷阱会响,取决于你用的是哪款拦截器、哪几份过滤列表,于是这个结果会连同你的字体指纹和 canvas 哈希,一并汇入追踪者为你建立的档案。广告拦截器不会让你隐身,它只会让你变成一个以某种具体、可测量的方式拦截广告的人。

到底该怎么做

  1. 先看清哪些检查会触发。 跑一遍本页的小工具,再做一次完整浏览器扫描,把周边的一切也查个遍:指纹哈希、WebRTC,一样不落。知道自己栽在哪几项检查上,正是”瞎猜”和”对症下药”的区别。
  2. 被冤枉了?先找到是哪一层。 停用所有扩展再刷新页面。如果仍然被检测到,说明拦截发生在更深的地方:DNS 过滤、浏览器默认设置,或是杀毒软件的网页防护。在一个你根本没装的扩展里给网站加白名单,永远修不好一条由路由器执行的拦截。
  3. 管理多个账号?留意你的拦截模式。 拦截模式是又一项必须在每个身份上保持一致的特征,一个会话接一个会话都不能走样。Incogniton 会把每个配置文件都做成一个完全独立的浏览器,拥有自己的扩展和设置,配置文件 A 的拦截习惯绝不会渗进配置文件 B 的指纹里。

下一次再有网站信誓旦旦地说你在用广告拦截器时,你就知道它真正抓到的是什么了:一个失去了高度的 div,或者一条一去不回的诱饵请求。而上方的小工具会告诉你,出卖你的究竟是哪一个。

Incogniton 徽标

提升你的隐私保护

箭头