Canvas 指紋是最強大的追蹤手段之一:網站讓瀏覽器繪製隱藏圖像,再用像素級差異生成穩定的裝置標識。了解它的運作原理,以及如何降低被追蹤的風險。
Canvas 指紋的原理,是讓瀏覽器在一塊看不見的 HTML5 畫布上繪製文字與圖形,再把算繪出的像素讀回來,雜湊成一段短小而穩定的識別碼。由於每一台裝置在執行同一套繪製指令時,都會因為 GPU、顯示卡驅動程式、作業系統和字型引擎的差異而產生略有不同的結果,這段雜湊便成了一個悄無聲息、不需任何 Cookie 就能跨站跟著你的簽章。本文會比基礎介紹走得更深:像素究竟在哪裡產生分歧、Canvas 到底貢獻了多少獨特性、如何判斷自己正在被指紋採集,以及為什麼某些常見的防禦反而會弄巧成拙。
Canvas 指紋究竟如何運作
這項技術仰賴 <canvas> 元素——它原本是用 JavaScript 繪製圖形的工具。追蹤指令碼根本不需要把畫布顯示在螢幕上,它完全存在於記憶體之中。
幾乎所有實作的流程都高度一致:
- 建立一塊離螢幕畫布。 指令碼建立一塊很小的畫布(常見寬度為 200 到 300 像素),但從不把它顯示地掛到頁面上。
- 繪製一個固定場景。 指令碼算繪一段預先設定好的文字,常常混用不同字型、顏色、一兩個表情符號以及彼此重疊的矩形。表情符號和冷僻 Unicode 之所以受青睞,是因為它們會迫使系統字型堆疊做出算繪決策。
- 把像素讀回來。 指令碼呼叫
toDataURL()把畫布匯出為 Base64 編碼的 PNG,或用getImageData()讀取原始的 RGBA 位元組陣列。 - 雜湊結果。 這些位元組被送入雜湊函式(常見的有 FNV 或 MurmurHash),產生一段精簡的指紋,例如
e3b0c44298fc1c14。
精妙之處在於:指令對所有人都一模一樣,但輸出卻不同。兩位執行完全相同指令碼的訪客可能得到不同的雜湊;而同一位訪客每次回訪都會得到相同的雜湊——這正是追蹤者想要的。
這塊畫布從不顯示。你看不到任何閃爍、方框或視覺提示。正是這種隱蔽性,讓 Canvas 指紋成為靜默追蹤的常用手段。
為什麼像素會因裝置而異
如果繪製「Hello」本應是確定性的,那位元組為什麼會變化?答案是,你系統中的多個層級各自引入了細微而可重現的差異。
GPU 與顯示卡驅動程式
反鋸齒、次像素算繪以及曲線的填滿方式,都有一部分交給 GPU 處理。Intel 內顯、Apple 自研 GPU 與 NVIDIA 獨立顯示卡,對邊緣像素的取整方式各不相同。即便兩台機器使用相同型號的 GPU,只要驅動程式版本不同,結果也可能產生分歧。
字型點陣化
最大的單一影響因素是文字算繪。作業系統的字型引擎——Windows 上的 DirectWrite、macOS 上的 Core Text、Linux 上的 FreeType——決定了如何把字形外框轉成像素。字型微調(hinting)、伽瑪校正以及具體的反鋸齒演算法,都會隨平台和版本而變,因此字母「g」邊緣那些灰色像素,往往攜帶了出乎意料的區分資訊。
作業系統與已安裝字型
請求的字型是否存在,決定了瀏覽器是否會替換成後備字型。同一個表情符號,在某個系統上可能算繪成扁平的外框,在另一個系統上則是全彩字形。系統的彩色表情集(Apple、Noto、Segoe)尤其會留下強烈的印記。
瀏覽器引擎與版本
Chromium、Firefox 與 WebKit 各有自己的 Canvas 實作。色彩管理以及 toDataURL() 編碼 PNG 資料的方式,都可能隨瀏覽器版本而變——這也是為什麼瀏覽器一更新,你的 Canvas 雜湊有時就跟著變了。
Canvas 到底貢獻了多少獨特性
Canvas 對追蹤者有價值,並不是因為它能單獨識別你,而是因為它貢獻了相當可觀的一份熵——也就是衡量一個訊號能把「你是誰」縮小到什麼程度的指標。在對真實環境中指紋技術的研究裡,Canvas 一再被列為被動網站可讀取的單項訊號中熵值最高的一檔,與完整的已安裝字型清單不相上下。
話雖如此,Canvas 很少能憑一己之力鎖定一個人。許多人共享著常見組態——一台裝著預設字型、用 Chrome 的標準 Windows 筆電,會與數以百萬計的人撞在一起。它真正的威力體現在組合之中:Canvas 加上 WebGL、螢幕參數、時區和語言,合起來便逼近一個近乎獨一無二的指紋。想全面了解這些訊號如何疊加,可參閱我們的瀏覽器指紋完整指南。
| 屬性 | Canvas 指紋 |
|---|---|
| 是否需要儲存 | 不需要(無 Cookie、無 localStorage) |
| 清除快取/Cookie 後是否仍有效 | 是 |
| 無痕/隱私模式下是否仍有效 | 通常是 |
| 使用者是否看得到 | 否 |
| 典型熵貢獻 | 在單項被動訊號中名列前茅 |
| 清除資料能否破解 | 否 |
| 更換 IP 能否破解 | 否 |
網站如何使用 Canvas 指紋
Canvas 指紋具有雙重用途,同一套機制既可用於侵犯隱私,也可用於保護安全:
- 跨站追蹤。 嵌入在眾多網站中的廣告與分析網路讀取 Canvas 雜湊,即便使用者清除了 Cookie,也能認出回訪者,並據此建立行為輪廓。
- 反詐騙與帳戶安全。 銀行和支付機構會標記那些 Canvas 指紋與已知裝置突然不符的登入,協助識別帳戶盜用。
- 機器人與濫用偵測。 無頭瀏覽器和自動化框架產生的 Canvas 輸出往往要麼過於一致,要麼以某些破綻十足的方式算繪,因此指紋有助於把真人和指令碼流量區分開——關於這在實務中如何運作,請參閱機器人偵測技術。
- 流量限制與防濫用。 服務把指紋當作一個穩定的鍵,用來限制那些靠不斷更換 Cookie 來規避配額的帳戶。
如何判斷自己正在被 Canvas 指紋採集
你看不到那塊畫布,但可以留意伴隨它出現的行為。
留意 API 呼叫
在一次指紋採集嘗試中,JavaScript 會在一塊從未被加入可見頁面的畫布上,先呼叫 getContext('2d'),接著是 fillText(),最後是 toDataURL() 或 getImageData()。瀏覽器開發者工具和某些隱私擴充功能能夠把這些呼叫揭露出來。
一個極簡的偵測掛鉤
你可以自己給 Canvas API 加上探針,記錄可疑的讀取行為:
// 偵測那些讀取畫布像素卻什麼都不顯示的指令碼
(function watchCanvasReads() {
const origToDataURL = HTMLCanvasElement.prototype.toDataURL;
const origGetImageData = CanvasRenderingContext2D.prototype.getImageData;
HTMLCanvasElement.prototype.toDataURL = function (...args) {
if (!document.body.contains(this)) {
console.warn('[canvas-fp] 在離螢幕畫布上呼叫了 toDataURL()', this);
}
return origToDataURL.apply(this, args);
};
CanvasRenderingContext2D.prototype.getImageData = function (...args) {
console.warn('[canvas-fp] getImageData() 正在讀回像素', this.canvas);
return origGetImageData.apply(this, args);
};
})();
如果你更想直接看到自己的指紋,而非閱讀程式碼,最簡單的辦法是執行 BrowserInsight 的指紋檢測工具。它會即時計算你的 Canvas 雜湊,把 WebGL 與音訊訊號一併呈現出來,並估算你整體指紋的獨特程度。
各種防禦手段及其取捨
世上沒有十全十美的防禦,而且——這一點尤為關鍵——某些熱門方案反而會讓你更容易被識別。以下是主流方法的比較。
趨同(Tor 瀏覽器的思路)
Tor 瀏覽器的策略,是讓每個使用者看起來都一模一樣。它預設停用 Canvas 讀回,並在允許之前跳出提示,於是指令碼要麼得到空白結果,要麼得到一個被整個 Tor 群體共享的值。融入一個龐大而趨同的人群,是最穩健的防禦——但代價是一種高度標準化、有時相當受限的瀏覽體驗。
隨機化(Brave 的 farbling)
Brave 走的是另一條路,稱為 farbling:它對 Canvas 讀回按工作階段、按網域加入微小雜訊,使你的指紋在每個網站、每個工作階段都不相同,從而有效切斷跨站關聯。其中微妙的風險在於——「被隨機化」本身就是可被偵測的——若隨機化實作得過於粗糙,反而會增加熵,因為「一台 Canvas 帶雜訊的瀏覽器」本身就是一項區分特徵。設計良好的 farbling 會把雜訊控制得既小又合理,以避開這個陷阱。關於雜訊設計如何決定隨機化有效還是弄巧成拙的深入分析,參閱 Canvas 雜訊 vs 真實雜湊:隨機化為何適得其反。
攔截擴充功能(CanvasBlocker)
像 CanvasBlocker 這樣的擴充功能,可以攔截讀回、傳回偽造值,或按網域隨機化。它給了你細緻的控制權,但同樣的告誡依然成立:一個異常或過於激進的偽裝會顯得格格不入,設定不當的攔截器,可能比它想隱藏的 Canvas 還要醒目。
停用 JavaScript
透過 NoScript 之類的工具關閉 JavaScript,能徹底擊敗 Canvas 指紋,因為該 API 根本無法執行。代價相當高昂——大多數現代網站離開它就會壞掉——因此這只適合對安全極度敏感的情境。
| 防禦手段 | 運作原理 | 主要取捨 |
|---|---|---|
| Tor 瀏覽器趨同 | 讓所有人看起來都一樣 | 體驗受限、高度標準化 |
| Brave farbling | 按工作階段加雜訊以切斷關聯 | 隨機化本身可能被偵測 |
| CanvasBlocker | 攔截/偽造/隨機化讀回 | 設定不當會弄巧成拙 |
| 停用 JavaScript | API 根本不執行 | 大多數網站會壞掉 |
常見問題
清除 Cookie 能去掉我的 Canvas 指紋嗎?
不能。Canvas 指紋不在你的裝置上儲存任何東西——它是從你的硬體和軟體如何算繪圖形中推導出識別碼的。清除 Cookie、快取或網站資料對它毫無影響。
無痕或隱私模式能阻止 Canvas 指紋嗎?
通常不能。無痕視窗會隔離 Cookie 和歷史記錄,但你的 GPU、驅動程式和字型並未改變,因此 Canvas 雜湊通常與一般視窗裡的一樣。無痕模式對付的是以儲存為基礎的追蹤,而非指紋。
網站能看出我在用 Canvas 攔截器嗎?
有時能。如果你的 Canvas 傳回了一個明顯偽造、空白或帶雜訊的值,老練的指令碼就能推斷出有保護正在生效。這也是為什麼趨同(看起來和大家一樣)往往比隨機化(看起來刻意與眾不同)更穩健。
Canvas 指紋和 WebGL 指紋是同一回事嗎?
兩者相關但不相同。Canvas 讀取的是文字與圖形的 2D 算繪;WebGL 則透過 3D 算繪探測你的 GPU,並直接暴露廠商與算繪器字串。在一個組合指紋中,兩者相輔相成——我們的 WebGL 指紋深度解析 詳細講解了 3D 這一側。


