DuckDuckGo 瀏覽器會為 Canvas 加雜訊並固定部分硬體值,但不會讓你和所有人一模一樣。開源程式碼改了什麼、沒改什麼。
許多人在 DuckDuckGo 瀏覽器裡開啟指紋檢測頁,只想知道一件事:我的指紋受到保護了嗎?簡短的答案是:這款瀏覽器會削弱一部分指紋訊號,但並不像 Tor Browser 那樣試圖讓所有使用者看起來完全一致。它的保護混合了兩種策略,兩者都能在開源程式碼中看到。
核心要點
- DuckDuckGo 瀏覽器的保護以頁面腳本的形式注入,來自開源儲存庫 content-scope-scripts,開關由 privacy-configuration 中遠端下發的設定控制。
- 策略一是加雜訊:Canvas 讀回的像素會被改動,改動依據網站與瀏覽工作階段產生,因此雜湊值隨網站、隨工作階段而變。
- 策略二是固定值:
hardwareConcurrency、deviceMemory、部分screen屬性與 Battery API 對所有人回傳相同的答案。 - 這些都不會讓你「變得一致」。本文涉及的功能沒有動到瀏覽器引擎、User-Agent、WebGL 渲染器與字型。
- 行為可能因平台與網站而異,因為設定裡有依功能的開關與依網域的例外清單。
兩種策略,而非一種
指紋防禦通常在兩種思路間選擇。一致性讓許多使用者回傳相同的值,這個值就不再能識別任何人,Firefox 與 Tor 的 resistFingerprinting 就是這樣;雜訊則擾動輸出,讓追蹤者拿不到穩定的值,雜訊的利弊見這裡。
DuckDuckGo 依功能逐項混用兩者:原始值熵很高、本來就會變動的(Canvas 像素)用雜訊;一個數字就能輕鬆歸一的(核心數、記憶體、色彩深度)用固定值。這是務實的折衷,目標是減少跨站追蹤又不至於弄壞太多網站,而不是匿名承諾。
程式碼改了什麼
每個功能都是 content-scope-scripts 中 injected/src/features/ 底下的一個小檔案,並對應 privacy-configuration 中 features/ 底下的一個 JSON 檔。
Canvas(fingerprinting-canvas.js)。腳本包裝了 getImageData、toDataURL 與 toBlob。只有在 fillText、arc、漸層建立等繪圖呼叫之後,畫布才會被視為可疑。讀回這類畫布時,腳本把它複製到離屏畫布並在那裡修改像素,金鑰由工作階段金鑰與網站網域構成(對像素檢查碼計算 HMAC)。改動極小:對一部分像素翻轉某個顏色通道的最低位元,並跳過與鄰近像素相同的像素。雜湊變了,畫面看起來不變。由於金鑰在一次工作階段內穩定,同一畫布讀兩次得到相同位元組,避開了我們的 Canvas 雜訊文章談到的「二次讀取」破綻。共享的 fingerprinting-canvas.json 還把 webGl 設為 "enabled",因此 shaderSource、drawArrays 等 WebGL 呼叫也會把畫布標為可疑,以 toDataURL 匯出的 WebGL 渲染圖同樣會被加雜訊。不過這只作用於渲染出的像素,GPU 廠商與渲染器字串並不受影響。
音訊(fingerprinting-audio.js)。程式碼會對 getChannelData、copyFromChannel 及分析器方法回傳的取樣值加上極小的偏移,同樣由工作階段與網域決定。但在共用的 fingerprinting-audio.json 中,該功能的 state 在撰寫時是 "disabled",所以別預設音訊雜訊已生效。這個訊號會洩漏什麼,見音訊指紋。
硬體(fingerprinting-hardware.js)。navigator.keyboard 被設為 undefined,hardwareConcurrency 與 deviceMemory 回傳設定裡的數字。共用設定目前設為 4 和 8;由於是遠端更新,請自行重讀 JSON。背景見 hardwareConcurrency 與 deviceMemory。
螢幕(fingerprinting-screen-size.js)。availWidth 與 availHeight 被強制為整個螢幕的寬高,availTop、availLeft 為 0,colorDepth、pixelDepth 為 24,藉此隱藏工作列與 Dock 的尺寸;多螢幕下的 screenX/screenY 也會被歸一。螢幕寬高本身仍會洩漏,見螢幕解析度指紋。
電池與儲存空間。 fingerprinting-battery.js 回報「已充飽且正在充電」,並讓事件失效;fingerprinting-temporary-storage.js 把 webkitTemporaryStorage.queryUsageAndQuota 回報的配額上限設為 4 GB,削弱對磁碟大小的推斷。
坦白說的限制
遠端設定。 每個功能都受設定控制。Canvas 腳本甚至在 additionalEnabledCheck 設定未開啟時直接返回,音訊功能在共用檔案裡是停用的。因此實際生效哪些保護,取決於平台版本與當前設定,而不只是程式碼。
依網站的例外。 設定列出了因弄壞網站而關閉某項保護的網域。光是 fingerprinting-hardware.json 就列了 15 個,例如 gamestop.com 的原因是「點擊加入購物車會短暫轉圈,商品卻沒有加入購物車」。在這些網站上,該保護是關閉的。
未涉及的面向。 上述檔案都不改 User-Agent、WebGL 渲染器字串或已安裝字型清單,瀏覽器本身建立在平台內建的網頁引擎上。這些訊號仍會構成指紋。本文查看的檔案裡沒有針對它們的功能,但我們沒有稽核整個專案,所以請理解為「這些功能未涵蓋」,而不是「從未保護」。
固定值也可能顯眼。 在 16 核心桌機上回報 4 核心,是腳本可以與其他訊號交叉驗證的值,這種不一致本身就可能成為訊號,見指紋硬體不一致。
橫向比較
| 瀏覽器 | 主要思路 | 例子 |
|---|---|---|
| DuckDuckGo | Canvas 加雜訊,少數屬性固定值 | Canvas 位元翻轉、固定核心數 |
| Brave | 跨多個 API 的依工作階段、依網站「farbling」 | 見 Brave 隱私更新 |
| Firefox / Tor | 一致性(resistFingerprinting) | 計時精度取整、標準視窗尺寸 |
這是策略的比較,不是替誰打分數。底層技術請先讀瀏覽器指紋技術完全指南。另外澄清:DuckDuckGo 搜尋引擎是另一個產品,它的隱私承諾針對搜尋紀錄,而非瀏覽器指紋。
自己測一測
在 DuckDuckGo 瀏覽器裡開啟我們的指紋檢測,記下 Canvas 雜湊,然後在新的工作階段重新造訪。被加雜訊的 Canvas 雜湊應該跨工作階段變化,而核心數、裝置記憶體之類的值維持固定。我們不會標註「偵測到 DuckDuckGo 保護」:工具只呈現原始值,結論由你自己得出。
常見問題
DuckDuckGo 瀏覽器能完全阻止指紋追蹤嗎?
不能。它會改動部分讀回值並歸一部分屬性,但引擎、User-Agent、WebGL 字串等訊號不在本文所述功能範圍內。
我的 Canvas 指紋在每個網站都一樣嗎?
如果 Canvas 保護已生效就不一樣:雜訊金鑰包含網站網域,所以同一幅繪圖在不同網站、不同工作階段會得到不同雜湊。
DuckDuckGo 的保護在每台裝置上都相同嗎?
不一定。功能透過依平台遠端下發的設定啟用,行為可能因作業系統與應用程式版本而異。


