Chrome 正在試驗把真實 HTML 繪入 canvas 的 API。提案規定了哪些內容不得進入像素、哪些仍可讀取,本文逐項解讀。
Chrome 正在試驗一個新 API,讓 <canvas> 不再只能畫線條、矩形和文字串,而是可以直接繪製帶樣式的真實 HTML。這立刻帶來一個隱私問題:畫布的像素可以被讀回,而讀取畫布像素正是 Canvas 指紋的基礎。如果畫布裡現在能裝下一個渲染好的頁面,腳本能從中讀出什麼?提案作者給出了一份明確的清單,列出絕對不能被繪製的內容。本文逐項解讀這份清單及其含義。請注意:這是一個實驗,不是已發布的功能。
核心要點
- HTML-in-Canvas 是 Chrome Origin Trial(源試用) 中的提案,不是已發布的 Web 標準。依 Chrome 官方文章,試用期延長到 Chrome 160,且方法名已經改過(例如在 Chrome 155 中)。
- 核心隱私規則叫「允許回讀的渲染」(read-back-allowed rendering):繪製元素時,不得洩露頁面程式碼本來無法觀察到的敏感資訊。
- 說明文件將跨來源內容、系統顏色與主題、拼字標記、已造訪連結狀態、待處理的自動填入以及子像素文字抗鋸齒排除在繪製結果之外。
- 同時它有意保留了它認為「本來就能偵測」的資訊,例如捲軸與表單控制項外觀、游標閃爍頻率和
forced-colors。 - 這不是一種現在就存在的指紋技術,而是一個試圖避免製造新指紋面的設計。
這個 API 做什麼
今天的畫布只能繪製基本圖元。你可以用 fillText() 畫一串文字,卻無法交給它一個帶版面、換行和表單控制項的 <div>。HTML-in-Canvas 提案在 W3C 的 WICG 儲存庫中開發,新增了對元素做快照並繪入 2D、WebGL 或 WebGPU 畫布的能力,2D 情境下的方法叫 drawElementImage()。Chrome 的源試用變更文章指出,試用延長到 Chrome 160,部分方法和屬性名在 Chrome 155 中改過,所以你看到的任何程式碼都只是暫定寫法。該功能在 Chrome Platform Status 上也有條目。
它的動機主要是創意與實用:替真實 HTML 加特效、在 3D 場景裡放文字、在畫布中保留可存取的內容。隱私是設計的限制條件,而不是目標。
為什麼畫布是隱私敏感面
畫布不只是輸出。腳本可以呼叫 toDataURL() 或 getImageData(),把每個像素當作資料取回。Canvas 指紋正是這樣運作的:繪製固定場景、讀回、做雜湊。雜湊之所以不同,是因為 GPU、驅動程式、字型點陣化器和作業系統各不相同。更完整的背景見我們的瀏覽器指紋指南。
現在設想畫布裡能裝下一個渲染好的頁面。瀏覽器會繪製一些頁面腳本無法直接看到的東西:某個連結是否造訪過、作業系統主題是什麼顏色、拼字檢查有沒有替某個詞畫底線。如果這些洩露進繪製出的像素,腳本就能把它們讀出來,一個看似無害的繪製呼叫就變成了窺探私有狀態的窗口。
「允許回讀」規則
說明文件用一條原則處理這個問題,稱為 read-back-allowed rendering。依其原文,繪製元素及相關的 paint 事件「不得洩露頁面作者程式碼本來無法觀察到的、與安全或隱私相關的敏感資訊」。由於 WebGL 和 WebGPU 總是可以回讀像素,安全的做法就是一開始就不把敏感資訊畫進輸出。文件把它表述為防止透過像素回讀、計時攻擊和失效事件造成洩露。文件並未直接提到「指紋」一詞;它是一條安全與隱私規則,下文的指紋視角是我們自己的解讀。
哪些內容被排除在像素之外
說明文件列出了必須排除在繪製之外的內容:
- 跨來源資料:iframe 和圖片等嵌入內容、
background-image這類 URL 參照、被跨來源資料污染的畫布,以及 SVG 參照。同源 iframe 仍會繪製,但其中的跨來源內容不會。 - 系統顏色、主題或偏好設定。
- 拼字和文法標記。
- 已造訪連結資訊。
- 待處理的表單自動填入資訊(JavaScript 本來看不到的部分)。
- 子像素文字抗鋸齒。
- 字幕和影片控制項。
- 輸入法(IME)彈出視窗及有辨識度的輸入法文字格式。
其中多數是顯而易見的洩露隱憂(已造訪連結和自動填入是經典的歷史嗅探目標)。對指紋來說,最有意思的是子像素抗鋸齒這一項。平台如何平滑文字邊緣取決於作業系統、螢幕和設定,這也是同一段文字在不同機器上看起來略有差異的原因之一。畫布上一般的 fillText() 已經暴露了不少這類渲染行為,它是字型與文字渲染指紋的核心素材。把它從新路徑中排除,意味著新 API 不會增加一種更高解析度的讀取方式。
這個對比很重要:提案並沒有修復現有的 Canvas 指紋,它只是拒絕把這個攻擊面做大。
有意保留的內容
說明文件還列出了在這一語境下它不認為敏感的資訊:
- 頁內尋找文字與文字片段(text fragment)標記;
- 捲軸與表單元素外觀,文件稱這些在 Blink 和 WebKit 中已可透過
foreignObject偵測; - 游標閃爍頻率;
forced-colors,文件指出 JavaScript 已能透過媒體查詢和系統顏色取得這項資訊。
邏輯是一致的:如果頁面程式碼本來就能觀察到某個值,把它畫出來並不增加新資訊。這是站得住腳的規則,但也暴露了邊界。forced-colors 之類的訊號本來就在 CSS 媒體查詢指紋蒐集的範圍內,所以無論如何追蹤者都能拿到。提案並沒有宣稱要減少它們。
GPU 路徑
GPU 一側同樣存在回讀問題,文件明確說 WebGL 和 WebGPU 總是可以回讀像素。因此這些排除規則也適用於把元素快照繪入這兩類情境的變體:目前 WebGL 用 texElementSubImage2D(),WebGPU 用 drawElementImageToTexture(),兩個名稱都是 Chrome 155 改過的。想了解這些情境本來就洩露什麼,可以看我們的 WebGL 指紋深度解析和 WebGPU 指紋。
客觀的侷限
- 這是提案與實驗。Chrome 的試用可能改變或結束,隱私審查也可能修改排除清單。
- 在我們引用的頁面上,沒有找到其他引擎的承諾,因此我們不對 Firefox 或 Safari 的立場做任何斷言。
- 寫在設計文件裡的規則,品質取決於實作。計時、失效通知、特殊元素類型等邊緣情況恰恰是這類規則容易漏的地方,這也是說明文件明確點名計時攻擊的原因。
- 這裡的任何內容都不會改變 Canvas 指紋今天能做到的事。
這對你意味著什麼
對訪客:目前什麼都不會變。Origin Trial 只在註冊了試用的網站上、且只在試用期內的 Chrome 版本中生效,一般使用者無需也無法自行開啟。如果你想無論如何都降低 Canvas 指紋風險,請閱讀 Canvas 雜訊與隨機化為何可能適得其反,並優先選擇「一致」而不是「隨機」。
對開發者:如果你試用這個 API,請假定名稱是暫定的,並且不要指望能在畫布裡擷取敏感狀態——這是有意為之的設計。
查看你自己的 Canvas 雜湊
BrowserInsight 的指紋檢測使用標準 2D Canvas API 計算 Canvas 雜湊,並未使用 HTML-in-Canvas。藉此可以看到,腳本讀取一個一般畫布時,今天就能從你的瀏覽器拿到什麼。

