Bromure 如何在頁面看見之前就攔下廣告
多數廣告攔截器都是瀏覽器擴充功能,而多數擴充功能都和它想保護你的那個頁面跑在同一個行程裡。Bromure 的做法不一樣。以下是怎麼做到的,以及為什麼這件事重要。
住在瀏覽器裡的廣告攔截器,就像想從擂台內部當裁判。Bromure 把裁判搬到網路本身——一個頁面碰不到、指令碼觸不到、也沒有「反廣告攔截」橫幅能跟它吵嘴的位置。
廣告攔截是最受歡迎、卻沒有內建於瀏覽器的瀏覽器功能。數以億計的人一拿到新電腦就裝上第三方擴充功能,因為現代網路若沒有它,根本無法使用。這件事本身,就是一個設計上的異味。但幾乎每一款廣告攔截器的運作方式,都值得我們好好端詳一番,因為架構這件事其實影響深遠——既影響它實際能發揮多少效果,也影響當頁面主動反擊時會發生什麼事。
這篇文章會走過一般廣告攔截器是怎麼運作的、這種形態為什麼有實實在在的極限,以及 Bromure 如何採取另一種做法——這個做法的核心,其實借自一個你或許在某人地下室的 Raspberry Pi 上聽過的小工具。
廣告不只是惱人。它們是載入問題、隱私問題,也是一種攻擊途徑。
用了幾年廣告攔截之後,人很容易忘記原始、未經過濾的網路長什麼樣子。一篇熱門新聞文章,若不加任何過濾地載入,通常會從 30 到 80 個第三方網域抓資源。其中大多是分析服務、追蹤像素,以及即時競價端點。它們讓頁面變重、延遲變高,替一堆你連名字都沒聽過的公司種下 cookie,而且——正如本月稍早那篇關於勒索軟體的文章所提——它們是惡意程式其中一條低調的遞送管道,因為廣告聯播網偶爾會被騙,在正當網站上投放含有惡意內容的素材。
因此,攔下這些請求並不是美觀層面的問題。它能讓效能提升、讓隱私提升,而且——因為廣告聯播網是惡意程式的常見散布管道——它也實實在在讓資安提升。唯一的問題是:你要怎麼攔。
典型的瀏覽器擴充功能是怎麼攔廣告的。
幾乎每一款主流廣告攔截器,包括你大概裝過的那些熱門款,都是瀏覽器擴充功能。這意味著在架構上,它住在瀏覽器裡面——它在瀏覽器行程內執行 JavaScript,針對瀏覽器的網路 API(webRequest、declarativeNetRequest)註冊回呼,並檢查瀏覽器即將發出的每一個請求。
這個形態能用——畢竟多數人每天都仰賴它——但它有實實在在的結構性限制:
頁面看得出來
既然擴充功能和頁面跑在同一個行程裡,頁面自己的 JavaScript 就能探測:「我的請求真的送出去了嗎?這個變數最後有被定義嗎?」整票網站(以及多數有付費牆的新聞媒體)會偵測到缺少的請求,然後拒絕載入,要求使用者「停用你的廣告攔截器」。
頁面和它在同一個信任區裡
擴充功能與它試圖過濾的那個惡意頁面,共用一個瀏覽器行程、一個轉譯程序,有時還共用一組權限。若頁面上出現夠糟糕的瀏覽器漏洞利用,擴充功能會跟著其他東西一起陣亡。擴充功能是程式碼;它們同時也是攻擊面。
請求早就已經誕生
等擴充功能看到某個請求時,瀏覽器其實已經決定要發這個請求、已經解析完網址,並且(視使用的 API 而定)做完 DNS 查詢。在那個階段才攔,是在過濾,不是在預防。
過濾 API 愈來愈弱
Chrome 在 Manifest v3 改寫之後,限縮了過濾類擴充功能能做的事,主要是限制規則數量,並以 declarativeNetRequest 取代動態的 webRequest。擴充功能 API 的架構是由瀏覽器廠商決定的,不是你能決定的。
Bromure 的做法:那道牆不是擴充功能。
Bromure 的廣告攔截器不是擴充功能。瀏覽器本身裡面完全沒有任何廣告攔截相關的程式碼。取而代之的是:瀏覽器執行所在的那台用完即丟 Linux VM 的網路堆疊才是那個廣告攔截器——從瀏覽器的角度來看,廣告網域根本就無法解析。
在機制上,有兩個不起眼的小軟體在幹活:
一個本地 HTTP 代理伺服器(Squid)
瀏覽器送出的每一個請求,都會被透明地導向 VM 內跑在 localhost:3128 上的 Squid 代理伺服器。Squid 扮演的是管線的角色:它不決定什麼算廣告,只為瀏覽器做 DNS 解析與轉送而已。
一個本地 DNS 解析器(dnsmasq)
我們指示 Squid 透過跑在 127.0.0.1 的本地 dnsmasq 來解析每一個網域。dnsmasq 的設定裡包含一份大約十萬個網域的封鎖清單——廣告聯播網、追蹤器、分析服務,以及已知的惡意主機。清單上的一切,都會被解析到 0.0.0.0,而那哪裡都去不了。
結果就是:從瀏覽器的角度看,這是一個長得像網際網路的網路,只是廣告與追蹤網域完全沒有 DNS 記錄而已。瀏覽器問 doubleclick.net,拿到「沒有路由」,然後就過了。沒有連線被開啟。沒有 TLS 交握被嘗試。沒有任何 cookie 被送出去,因為根本沒有「去」可送。
這個點子,在地下室裡已經跑了好多年。
如果你曾經架設過 pi-hole——那個很多人放在 Raspberry Pi 上、負責把整個家用網路裡的廣告過濾掉的小盒子 DNS 黑洞——那你會覺得 Bromure 的架構非常眼熟。底層那份封鎖清單就是 Steven Black 的統一 hosts 檔案,也就是那些家用安裝已經跑了好幾年的同一份。概念上的轉變完全一樣:別想著把廣告從頁面裡移除,直接拒絕告訴瀏覽器廣告的伺服器在哪裡,就好了。
重要的差異在於:這個 DNS 黑洞住在哪裡。家戶級的 DNS 黑洞保護的是家用網路上的每一台裝置;Bromure 的 DNS 黑洞只保護某一個特定的瀏覽器設定檔。你可以在用來讀新聞的那個設定檔打開它;在你銀行大概不太希望你經過任何奇怪中介層的那個設定檔關掉它。
這樣做實際換到的是什麼。
實務上能拿到的好處一層又一層:
不會被被攻陷的頁面停用
封鎖清單不是瀏覽器載入的程式碼,而是瀏覽器的網路本身。轉譯程序漏洞、惡意指令碼、行為異常的第三方函式庫——沒有一個能碰到 dnsmasq 的設定,因為沒有一個能跨出瀏覽器行程、觸及 VM 的網路堆疊。
頁面繞不過它
頁面的 JavaScript 可以一整天都在檢查 navigator 屬性;但它看不到所在網路的 DNS 設定,因為那不是 JavaScript 被允許知道的東西。對頁面而言,被封鎖的廣告看起來,就跟某個廣告網域剛好掛掉一分鐘一模一樣。
沒有擴充功能的攻擊面
廣告攔截器擴充功能曾不止一次被攻陷——透過擴充功能商店、透過併購易主,或透過惡意更新。Bromure 裡沒有這樣的擴充功能可以被攻陷,因為它根本就沒有擴充功能。
以設定檔為單位,像其他任何能力一樣可切換
廣告攔截是以設定檔為單位的設定(「隱私與安全」裡的「封鎖廣告」),預設關閉,你可以選擇在哪些設定檔打開它。你的銀行設定檔不做過濾;你的日常閱讀設定檔則乾乾淨淨。
誠實面對它的極限。
這個領域裡沒有什麼是免費的。以下是網路層做法刻意不做的幾件事,先跟你說清楚:
沒有外觀過濾
傳統擴充功能能把橫幅原本所在的那個空盒子藏掉。Bromure 不會改寫頁面,所以如果出版者預留了一塊 728×90 的區域,那塊空間還是會留在那裡,空著。在少數網站上這會造成一點小小的視覺困擾,而換來的是:頁面裡沒有任何東西能讓指令碼察覺異樣。
第一方廣告仍會通過
如果某個網站是從自己的網域投放自家廣告(現在有些大型科技平台就是這樣做),以網域為單位的封鎖無法區分「文章」與「廣告」——它們都掛在同一個主機名底下。這是一個真實存在的缺口;在有需要的情境下,解法是在上面疊一層 content-script,這是我們正在研究的工作。
反廣告攔截牆仍會偵測到「不在」
有些網站不需要真的找到廣告攔截器。它們只要檢查某個特定指令碼有沒有載入,如果沒有,就拒絕顯示文章。對於這些網站,任何攔截器——不管是擴充功能還是網路層——都會被抓到。這是你和出版者之間的政策之爭,而不是你和攔截器之間的技術之爭。
封鎖清單隨 Bromure 一起出貨
gravity.list 會在映像檔建置時被打包進去,而且會在你更新 Bromure 時一起更新。它不會像一個專門、隨時運行的家用 DNS 黑洞那樣,每小時拉取最新規則。對於一份以主機名為基礎的清單來說,這通常沒什麼問題——廣告網域汰換的速度不像、比方說、惡意程式網域那麼快——但還是值得你知道一下。
整件事的輪廓。
更大的重點,不是說 Bromure 的廣告攔截器在規則比對上神奇地比 uBlock Origin 強。事實上,uBlock 的規則引擎更為精巧。重點在於封鎖發生在哪裡。擴充功能是一位客氣的裁判,拜託瀏覽器請不要載入這個東西。Bromure 則是那條路——那條請求原本會經過的路——在交流道入口就被靜靜封了起來,連駕駛還沒上車就封了。
在密封 VM 裡做廣告攔截,並不是 Bromure 的什麼新功能;它是這套架構從第一天起就存在的結果。每一個設定檔的網路層都是它自己的小小世界。那個世界裡可以有 WARP,可以有付費 VPN,可以有 Tor 傳輸層。而它也可以免費擁有——那個在別人地下室裡靜靜過濾了好幾年廣告的同一套 DNS 黑洞——只不過這回它裝好、設定好,而且只限定在你想要它存在的那個瀏覽器工作階段裡。
如果你想要長版的說明:封鎖清單是一個純文字檔案,打包在 VM 映像檔裡——你可以在下一次發行版中直接檢視它,讀遍那十萬個將會悄悄從你瀏覽器的世界裡消失的網域。如果你只想要短版:在任一個 Bromure 設定檔裡打開「隱私與安全」,把「封鎖廣告」切到開啟。那條路已經被封了。你只需要上路,開車。