Web Audio API 當做 fingerprint 的方式

三年前的文章「How the Web Audio API is used for audio fingerprinting」講解了 AudioContext 是怎麼被拿來 fingerprint 的,最近在「How We Bypassed Safari 17's Advanced Audio Fingerprinting Protection」這篇看到的。

AudioContext 可以完全跟錄音設備無關,單純計算,然後因為不同瀏覽器實作上面有差異,就被拿來當作 fingerprint 了。

文章裡介紹的方法是透過 Oscillator 產生 440Hz 的正弦波,然後過 Compressor 降低音量 (運算):

The Web Audio API provides a DynamicsCompressorNode, which lowers the volume of the loudest parts of the signal and helps prevent distortion or clipping.

降低音量的運算再這塊各家的實作不同,就能夠區分不同的瀏覽器 (甚至是版本):

Historically, all major browser engines (Blink, WebKit, and Gecko) based their Web Audio API implementations on code originally developed by Google in 2011 and 2012 for the WebKit project.

Since then browser developers have made a lot of small changes. These changes, compounded by the large number of mathematical operations involved, lead to fingerprinting differences. Audio signal processing uses floating point arithmetic, which also contributes to discrepancies in calculations.

Additionally, browsers use different implementations for different CPU architectures and OSes to leverage features like SIMD. For example, Chrome uses a separate fast Fourier transform implementation on macOS (producing a different oscillator signal) and other vector operation implementations on different CPU architectures (used in the DynamicsCompressor implementation). These platform-specific changes also contribute to differences in the final audio fingerprint.

而這東西平常也不會用到,所以對 Tor Browser 這種特別重視 privacy 的瀏覽器就直接關掉他了:

Tor

In the case of the Tor browser, everything is simple. But unfortunately, web Audio API is disabled there, so audio fingerprinting is impossible.

透過 mDNS 建立內部網路的 fingerprint

Hacker News 上看到透過 mDNS 建立 fingerprint 的方式,進而定位使用者身分:「Brute-forcing a macOS user’s real name from a browser using mDNS (fingerprint.com)」,原文在「Demo: Brute-forcing a macOS user’s real name from a browser using mDNS」。

利用發 HTTP(s) request 出去時,雖然都是傳回 Failed to fetch 錯誤,但因為 hostname 存在時會是 connection timeout,而不存在時會直接因為 DNS 查不到而很快 failed 掉,這個時間差異產生了 side channel,可以透過時間差異知道某個 hostname 是否存在。

這個技巧配合字典就可以大量掃描 *.local 的 mDNS 網段,進而產生出內部網路的 fingerprint。

這個問題應該是有標準解法 (或是有被提案過的解法),就是不讓 internet domain 存取 local domain 的東西,像是避免 internet 上的網站透過 JavaScript 碰到 http://127.0.0.1:xxx/ 的機制。

應該是把 *.local 用同樣方式對待就能避開這個問題?

Mullvad 也推出了自家的瀏覽器

Hacker News 上看到 VPN 廠商 Mullvad 也推出了自家的瀏覽器,Mullvad Browser:「The Mullvad Browser (mullvad.net)」。

改自 Tor Browser,最底層是 Firefox,然後開起來看,內建了三個套件:

然後因為是基於 Tor Browser,所以許多的預設值會更偏向強化隱私性的設定,這點可以從 fingerprint.com 這邊測發現,每次重開瀏覽器會是不一樣的值,這代表用 canvas 的網站有一定機會會掛掉... 另外會有一些不方便的地方,像是時間相關的設定因為要隱藏 timezone,所以 server 端無法取得 client 的正確時間資訊。

而在 FAQ 裡面有提到,Mullvad Browser 不允許你透過 cookie 記錄登入資訊:

How do I stay logged into specific websites between sessions?
It’s not possible. It’s an action to combat tracking.

所以這個瀏覽器的定位不會是給你當作一般用的... 但這樣的話我更偏好用 Tor Browser?

Content Defined Chunking (CDC)

前幾個禮拜在 Hacker News Daily 上看到「CDC File Transfer (github.com/google)」這則,連結是指到 GoogleGitHub 專案上,裡面實做了 FastCDC 演算法,另外說明他們為什麼要解這個問題以及對應的成果:「google/cdc-file-transfer」。

Google 的人看起來像是是在 CI/CD 階段遇到頻寬上的問題 (從「The builds are 40-45 GB large.」這邊猜),用 scprsync 看起來都不能解,所以他們自己刻了 FastCDC 演算法來解。

但我對 Content Defined Chunking (CDC) 不熟,所以先查一下 CDC 是什麼東西,就查到 restic 這篇講得很清楚:「Foundation - Introducing Content Defined Chunking (CDC)」。

要計算 delta 很直覺的作法就是要切 chunk,而接著的直覺就是固定大小的 chunk 切開,像是這樣每 16 bytes 切一個 chunk:

0123456789abcdef 0123456789abcdef 0123456789abcdef 0123456789abcdef

如果其中一個地方有變化,但其他沒變化的話就可以透過 cryptographic hash function (像是 SHA-256) 確認 chunk 內容一樣,進而省下很多傳輸的頻寬:

0123456789abcdef 0123456789ABCDEF 0123456789abcdef 0123456789abcdef

但可以馬上看出來這個方法的大缺點是只能處理 replacement,很難處理 insert & delete 的部份,舉例來說,如果變更是在開頭的地方加上 ABC,就會造成 chunk 會完全不一樣,而導致全部都要再傳一次:

ABC0123456789abc def0123456789abc def0123456789abc def0123456789abc def

這邊其實是個經典的演算法問題:想要找出兩個 string 的差異 (把舊的內容當作一個 string,新的內容也當作一個 string)。

這個問題算是 Edit distance 類型的題目,但你會發現解 Edit distance 的演算法會需要先傳輸完整個 string 才能開始跑演算法,這就本末倒置了。

而另外一個想法是,放棄固定的 chunk 大小,改用其他方式決定 chunk 的邊界要切在哪裡。而 CDC 就是利用一段 sliding window + hash 來找出切割的點。

文章裡面提到的 sliding window 是 64 bytes,這邊就可以算出對應的 HASH(b0, b1, ..., b63),然後往右滑動變成 HASH(b1, b2, ..., b64),再來是 HASH(b2, b3, ..., b65),一直往右滑動計算。

接下來 restic 會看 hash 值,如果最低的 21 bits 都是 0 就切開,所以 chunk 大小的期望值應該是 2MB?(這邊不確定,好像不能直接用 2^21 算,應該用積分之類的方法...)

For each fingerprint, restic then tests if the lowest 21 bits are zero. If this is the case, restic found a new chunk boundary.

而這個演算法可以適應新增與刪除的操作,不會造成從新增或刪除後的資料都要重傳,只有自己這個 chunk 需要重傳 (可能前或後的 chunk 也會要)。

然後挑一下 hash function 的特性,就可以讓計算的速度也很快。這邊提到了 hash function 可以用 Rolling hash,可以很快的從 HASH(b0, b1, ..., b63) 算出 HASH(b1, b2, ..., b64),而不需要全部重算。

有了 chunk 後,再用 cryptographic hash function 比較 chunk 的內容是否一樣,這樣就可以大幅降低傳輸所需要的頻寬了。

CloudFront 支援 JA3 資訊 (SSL/TLS fingerprint)

看到 CloudFront 宣佈支援帶入 JA3 資訊了:「Amazon CloudFront now supports JA3 fingerprint headers」:

Details: Amazon CloudFront now supports Cloudfront-viewer-ja3-fingerprint headers, enabling customers to access incoming viewer requests’ JA3 fingerprints. Customers can use the JA3 fingerprints to implement custom logic to block malicious clients or allow requests from expected clients only.

JA3 的頁面上可以看到說明,針對 SSL/TLS 這個複雜的 handshake 過程中,就可以從中得知不同的 client 實做,比起容易偽造的 User-agent 有效:

JA3 is a method for creating SSL/TLS client fingerprints that should be easy to produce on any platform and can be easily shared for threat intelligence.

像是 Tor 的 SSL/TLS 連線就會有對應的 fingerprint 可以偵測:

JA3 fingerprint for the standard Tor client:
e7d705a3286e19ea42f587b344ee6865

先前在「修正 Curl 的 TLS handshake,避開 bot 偵測機制」裡面提到的 curl-impersonate 就是反制這類偵測的方式。

cURL 的 TLS fingerprint

看到「curl's TLS fingerprint」這篇,cURL 的作者 Daniel Stenberg 提到 TLS fingerprint。

先前在「修正 Curl 的 TLS handshake,避開 bot 偵測機制」與「curl 的 TLS fingerprint 偽裝專案 curl-impersonate 支援 Chrome 了」這邊有提到 curl-impersonate 這個專案,試著在 cURL 裡模擬市面上常見的瀏覽器的 TLS fingerprint。

在 Daniel Stenberg 的文章裡面也有提到這件事情,另外也提到了對 curl-impersonate 的態度:

I cannot say right now if any of the changes done for curl-impersonate will get merged into the upstream curl project, but it will also depend on what users want and how the use of TLS fingerprinting spread or changes going forward.

看起來短期內他是沒打算整,跟當初 curl-impersonate 的預期差不多...

利用字型來判斷使用者是否有安裝特定軟體

Hacker News 上的「TeamViewer installs suspicious font only useful for web fingerprinting (ctrl.blog)」這邊看到的技巧,原文在「TeamViewer installs suspicious font only useful for web fingerprinting」這邊,但文章標題本身可以忽略。

這別提到的方法是,在安裝軟體時額外安裝一個特別的字型,然後網頁就可以透過 javascript 判斷這個字型存不存在,來得知使用者是否有安裝自己的軟體,接下來就可以走到不同的 flow:可以導引使用者下載軟體,或是透過 handler 拉起應用程式。

不過這也透漏出了隱私問題,代表廣告商可以利用這點取得 fingerprint,而不只是軟體自家的網站。

看討論串裡面說 Firefox 上可以用 privacy.resistFingerprinting 擋住:「Firefox's protection against fingerprinting」,但 Firefox 本身也沒有說明的太清楚到底會放行哪些字型:

Not all fonts installed on your computer are available to webpages

在「Security/Fingerprinting」這頁則是:

We only allow specific system fonts to be used, and we ship them to the user using kinto

直接試著找 Bugzilla 與 source code 的資料可以翻到「Restrict CSS font visibility to standard fonts only when privacy.resistFingerprinting is true」這個討論,裡面有提到「https://searchfox.org/mozilla-central/search?path=StandardFonts*.inc」這個,可以看到有 LinuxmacOSWindows 10 下的清單。

不過 Chromium-based browser 下目前好像沒看到方案...

curl 的 TLS fingerprint 偽裝專案 curl-impersonate 支援 Chrome 了

先前在「修正 Curl 的 TLS handshake,避開 bot 偵測機制」這邊提到 curl-impersonate 這個專案,試著修改 curl 的 TLS handshake fingerprint 讓偽裝的更好,本來只支援 Firefox,現在則是支援 Google Chrome 的 fingerprint 了...

作者寫的兩篇說明文章也可以看看:「Making curl impersonate Firefox」、「Impersonating Chrome, too」。

看起來愈來愈完整了,連 LD_PRELOAD 的用法也出現了,然後在 Arch Linux 上也出現 AUR 可以用了...

修正 Curl 的 TLS handshake,避開 bot 偵測機制

利用 TLS handshake 的 pattern 可以當作是某種 fingerprint,就可以知道你是用 Curl,這個方式在蠻多 CDN 都會用在 anti-bot 機制 (像是 Cloudflare),而剛剛看到有人投稿自己的 patch,試著將 Curl 修改成 Firefox 的 pattern:「curl-impersonate」,Hacker News 上的討論在這邊可以看到:「Show HN: Curl modified to impersonate Firefox and mimic its TLS handshake (github.com/lwthiker)」。

作者有提到這次的 patch 偏 hack,不太可能整進上游,但希望未來改的乾淨一點,然後整進上游:

I hope to do so in the future, for now the implementation is extremely hacky so I doubt it can get accepted into curl.

另外有人提出來說應該要用 Firefox ESR 版本的 pattern 而非 stable channel,也有人提出來說用 Google Chrome 的更好,不過我覺得有人開始做就已經很棒了 XD

更多瀏覽器內 GPU fingerprint 的技巧

在「Your device's GPU may be used for fingerprinting purposes」這邊看到有研究單位找出更多 GPU fingerprint 的技巧,論文在「DRAWNAPART: A Device Identification Technique based on Remote GPU Fingerprinting」這邊可以翻到,另外一些 source code 可以在 GitHub 上的「Drawn Apart」這邊可以翻到。

看起來是透過 WebGL 去建立模型:

The researchers ran experiments on 2500 unique devices using the technique. They developed two methods, both of which use the Web Graphics Library (WebGL), which is supported by all modern web browsers.

接下來瀏覽器端應該會在研究後有更多反制機制被放進來...