返回所有文章
发布于 · 作者 Renaud Deraison

Bromure 如何在页面看见广告之前就把它拦下

大多数广告拦截器都是浏览器扩展程序,而大多数浏览器扩展程序,又恰好与它要保护你免受其害的那个页面,运行在同一个进程里。Bromure 的做法不一样。下面讲讲它是怎么做的,以及为什么这很重要。

一款活在浏览器里的广告拦截器,就像是在擂台上当裁判。Bromure 把裁判搬到了网络本身——那里页面够不着,脚本碰不到,任何「反广告拦截」横幅也跟它争不起来。

广告拦截是当今浏览器最受欢迎、却偏偏不随浏览器一起发行的功能。成百上千万人在安装浏览器的第一天,就装上了一个第三方扩展,因为现代的网络,如果没有它,简直无法正常使用。这件事本身,就是一股设计上的怪味。但几乎所有广告拦截器的工作方式,都值得我们多看一眼,因为它们的架构其实非常关键——既关乎拦截效果本身,也关乎当页面主动反击时,会发生什么。

本文将介绍一款广告拦截器通常是怎么工作的,这种形态又有哪些现实的局限,以及 Bromure 为何选择另一条路——它的核心思路,借鉴自一件你也许在某位朋友家地下室的 Raspberry Pi 上见过的工具。

广告不只是烦人。它是一个加载问题,一个隐私问题,更是一条攻击通道。

用过几年广告拦截之后,人们很容易忘了「未经过滤」的网络本来是什么样子。一篇热门新闻文章,在不加任何过滤的情况下打开,通常会从 30 到 80 个第三方域名加载资源。其中绝大多数是分析代码、追踪像素,以及实时竞价的接口。它们让页面变重,让加载变慢,还替一堆你从未听说过的公司往你电脑里塞 cookie,而且——正如本月早些时候那篇关于勒索软件的文章所谈到的那样——它们也是恶意软件一条悄无声息的投递通道,因为广告网络时不时就会被骗,在正规网站上分发恶意素材。

news.example.com一篇文章doubleclick.netgoogletagmanager.comfacebook.netadnxs.comscorecardresearch.comcriteo.nethotjar.comsegment.comchartbeat.compubmatic.comrubiconproject.comliveramp.comtaboola.comoutbrain.comnewrelic.comamplitude.commixpanel.com…还有 40 个每一个图标都意味着一个新域名、一次新的 DNS、一次新的 TLS、一整套新的 cookie。你并没有要任何一项。
2026 年,在不加任何过滤的情况下,一次页面访问实际会拉取什么。你正在读的那篇文章,通常是整页里最小的那笔请求。剩下的那一大堆,都是把这篇文章层层包裹起来的那套机器。

所以,拦下这些请求并不是个外观问题。它是一次性能上的胜利,一次隐私上的胜利,而且——因为广告网络正是恶意软件的一条常规分发通道——它还是一次实实在在的安全胜利。唯一的问题只是:怎么拦。

一款典型的浏览器扩展是如何拦广告的。

几乎所有主流的广告拦截器,包括你大概率装过的那些热门产品,都是浏览器扩展程序。这意味着,从架构上讲,它活在浏览器里——它在浏览器进程中运行 JavaScript,针对浏览器的网络 API(webRequestdeclarativeNetRequest)注册回调,逐一检查浏览器即将发出的每一个请求。

浏览器进程页面加载、运行脚本、请求各种资源广告拦截扩展程序逐一检查每个请求,返回放行或拦截网络TCP / TLS连往站点(已拦截)扩展就是浏览器加载进来的一段代码。一个被攻陷的渲染器,或一次被撤销的权限,就能把它干掉。恶意页面能察觉它是否装了,然后拒绝加载。互联网广告服务器 · 追踪器 · 分析
基于扩展的广告拦截器的典型工作方式。扩展活在浏览器里。每一次外发的请求都会交给它审批;它会根据自己的规则,拦下命中的那些。页面和扩展共享同一个进程,大多数情况下还共享同一套权限。

这种形态确实能用——毕竟大多数人每天就靠它——但它在结构上有几个实打实的局限:

页面能察觉到

由于扩展和页面跑在同一个进程里,页面自己的 JavaScript 就能去探测它:「我那个请求真的发出去了吗?这个变量到底有没有被定义?」不少网站(尤其是绝大多数付费墙新闻网站)会检测缺失的请求,没有就拒绝加载,要求用户「先关掉广告拦截器」。

页面和它处在同一信任域内

扩展和它要过滤的那个恶意页面,共享同一个浏览器进程、同一个渲染器,有时候还共享同一套权限。页面里一个足够严重的浏览器漏洞,会把扩展和其他一切一并端掉。扩展是代码;扩展也是攻击面。

请求已经被生出来了

当扩展看到一个请求时,浏览器其实早就决定要发了——URL 已经解析过,(看具体的 API)DNS 查询可能也已经做了。在这一步拦,是过滤,而不是预防。

过滤 API 正在越变越弱

Chrome 的 Manifest v3 改造限制了过滤扩展能做什么,主要做法是限制规则条数,并用 declarativeNetRequest 替代动态的 webRequest。扩展 API 的架构,是由浏览器厂商决定的——不是由你决定的。

Bromure 是怎么做的:那堵墙不是扩展。

Bromure 的广告拦截器并不是一个扩展。浏览器本身完全没有任何广告拦截代码。真正扮演广告拦截器角色的,是浏览器所运行的那个一次性 Linux 虚拟机里的网络栈——而从浏览器的角度看,广告域名干脆就没法解析。

客户机虚拟机 · 配置档案网络浏览器没有装任何广告拦截扩展Squid 代理:3128 · HTTP/HTTPS通过本地 DNS 解析dnsmasq本地 DNS 解析器命中的域名进入黑洞黑名单gravity.list10 万多个域名全部指向 0.0.0.0互联网news.example.comdoubleclick.netadnxs.com已拦截页面和它的脚本都看不到代理的另一边。从浏览器内部看,广告域名干脆就不存在。
在 Bromure 某个配置档案的虚拟机内部,浏览器发出的每一个外发连接都会被路由到一个本地 HTTP 代理。该代理通过一个本地 DNS 服务器来解析所有域名,而这台服务器的「拒绝」清单就是一份黑名单。如果域名在名单上,浏览器拿到的就是一个不可达地址,连接根本建立不起来——页面自己的脚本也一样。

从机制上讲,真正干活的是两块又小又无趣的软件:

一个本地 HTTP 代理(Squid)

浏览器发出的每一个外发请求,都会被透明地路由到虚拟机里运行在 localhost:3128 上的 Squid 代理。Squid 只是管道:它不决定什么是广告,只负责替浏览器做 DNS 解析和请求转发。

一个本地 DNS 解析器(dnsmasq)

Squid 被配置为通过 127.0.0.1 上的本地 dnsmasq 实例来解析每一个域名。dnsmasq 的配置里包含了一份大约 10 万个域名的屏蔽列表——广告网络、追踪器、分析服务、以及已知的恶意主机。名单上的域名都会被解析到 0.0.0.0,哪里都去不了。

最终的结果是:从浏览器的角度看,它连上的是一个「看起来像互联网」的网络,只不过广告和追踪类的域名在 DNS 里根本没有记录。浏览器向 doubleclick.net 发起查询,拿到「无路可走」,于是就放弃了。不会打开任何连接,不会尝试任何 TLS 握手。哪家的 cookie 也送不出去,因为压根就没有一个「送出去的目的地」。

这套思路,其实已经在人们家里地下室里跑了很多年了。

如果你曾经搭过 pi-hole——那台很多人放在 Raspberry Pi 上,用来替整个家庭网络过滤广告的小盒子 DNS 黑洞——你会对 Bromure 的架构感到格外眼熟。底层用的屏蔽列表,正是 Steven Black 整理的那份统一 hosts 文件,也是多年来那些家庭部署一直在用的那份。思路上完全一样:不去试图把广告从页面里剥掉,而是干脆拒绝告诉浏览器广告服务器在哪里。

关键的区别在于,黑洞放在哪里。一个覆盖整个家庭的黑洞,保护的是家里的每一台设备;而一个 Bromure 的黑洞,只保护一个浏览器配置档案。你可以在看新闻用的那个配置档案上把它打开;在银行宁愿你不要走任何奇怪中间件的那个配置档案里,把它关掉。

这到底能给你带来什么。

扩展把它拦下浏览器解析广告 URL。对广告域名执行 DNS 查询。连接尝试可能已经开始。扩展在最后一刻出手否决。页面能察觉到那个缺失的请求。广告服务器得知「有人从 IP X 试图连我们,结果放弃了。」Bromure 根本不问浏览器向本地 DNS 查询这个域名。dnsmasq 答复 0.0.0.0没有任何网络连接被打开。没有 TLS、没有 SNI、没有 HTTP 请求。页面无法把它和 DNS 故障区分开。广告服务器得知什么都不知道。根本没有连接可以观察。
同一次页面访问,两种不同的架构。扩展让请求先发起,再在最后一刻去过滤它。Bromure 则是这个问题压根就没问出口。广告服务器对 Bromure 用户一无所知——连他们尝试过都不知道。

实打实的好处一条接一条:

被攻陷的页面无法将其关闭

屏蔽列表不是浏览器加载的代码,它是浏览器所在的那张网络。一个渲染器漏洞、一段恶意脚本、一个行为不端的第三方库——它们谁也碰不到 dnsmasq 的配置,因为它们谁也冲不出浏览器进程、抵达不了虚拟机的网络栈。

页面看不穿它

页面里的 JavaScript 可以整天检查 navigator 属性,它就是没法知道所在网络的 DNS 配置——因为这本来就不是 JavaScript 被允许知道的事情。在页面看来,一个被拦下的广告,和一个宕机了一分钟的广告域名,看起来一模一样。

没有扩展带来的攻击面

广告拦截扩展不止一次被攻陷过——可能是通过扩展商店,可能是因为被收购,也可能是因为一次恶意更新。Bromure 里没有这样一个扩展可以被攻陷,因为根本就没有扩展。

按配置档案启用,像其他能力一样可切换

广告拦截是一个按配置档案的设置(「隐私与安全」里的「拦截广告」),默认关闭,你想在哪个配置档案里开就在哪个里开。你的银行档案保持不做过滤;你每天看新闻的那个档案则干干净净。

坦诚说说它的局限。

这一领域里没有白吃的午餐。在网络层这种做法里,有几件事是它有意不去做的,你也应该事先心里有数:

不做外观过滤

传统的扩展可以把原本放广告的那块空白盒子藏起来。Bromure 不会去改写页面,所以如果网站为一则 728×90 的广告预留了位置,那块位置就会留着,空在那儿。在少数几个网站上,这是一个轻微的外观小瑕疵;而代价换来的,是页面里什么都没有留给脚本去察觉。

第一方广告还会留下

如果一个网站从自己的域名上投放自家的广告(一些大型科技平台现在就这么做),域名层面的拦截就没法区分「文章」和「广告」——它们都挂在同一个主机名下。这是一个实打实的缺口;在真正重要的场景下,解法是在上面再加一层内容脚本层,这也是我们正在研究的方向。

反广告拦截墙仍能察觉到缺失

有些网站根本不去找广告拦截器在哪。它们只检查某个特定的脚本有没有加载,没加载就拒绝展示文章。对这类网站来说,任何拦截器——无论是扩展还是网络层的——都会被挡下。这是一场和发行方之间的政策之争,而不是和拦截器之间的技术之争。

屏蔽列表随 Bromure 一同分发

gravity.list 在镜像构建时就烘进去了,并随 Bromure 的更新而更新。它不像家里那种专门一直开着的黑洞那样,每小时就去拉最新的规则。对一份基于主机名的名单来说,这通常够用了——广告域名的更替速度远不如恶意软件域名——但这一点值得知道。

事物的形状。

更宽泛一点说,重点并不是 Bromure 的广告拦截器在匹配规则这件事上比 uBlock Origin 更神。事实上,uBlock 的规则引擎要精巧得多。真正的重点,在于拦截发生在哪里。扩展是一个礼貌的裁判,在恳求浏览器别去加载这个东西。Bromure 则是那条请求本该走的路,在司机还没上车之前,就悄悄把高速路的入口封了。

密封虚拟机里的广告拦截并不是 Bromure 的一项新功能;它只是这套架构从第一天就在那里的一个自然结果。每一个配置档案的网络层,都是它自己的一个小世界。这个世界里可以有 WARP,可以有付费的 VPN,可以有 Tor 传输。而它也可以——免费地——拥有那套多年来一直在人们家里地下室里悄悄过滤广告的 DNS 黑洞,已经替你安装好、配置好,并严格限定在你想让它生效的那个浏览器会话里。

如果你想看长版本:那份屏蔽列表只是一个纯文本文件,随虚拟机镜像一起发放——下一个版本发布时,你可以亲自翻开看看,一条一条读完那十万个即将在你浏览器里悄悄不复存在的域名。如果你只想要短版本:在任何一个 Bromure 配置档案里打开「隐私与安全」,把「拦截广告」打开就行。路已经封好了。你只管开车。