鍵盤配置指紋透過 getLayoutMap() 讀取你的實體按鍵配置——一種無聲、僅限 Chromium 的訊號,能暗示地區且極少變動。
鍵盤配置指紋讀取一個大多數人從未想過的訊號:你鍵盤所設定的實體配置,透過 Keyboard API 的 getLayoutMap() 方法暴露出來。網站可以藉此詢問「這個實體按鍵位置會產生什麼字元?」——而答案會揭露你系統層級的配置(QWERTY、AZERTY、QWERTZ、JIS 及其他數十種配置),且完全無需請求任何授權。本文將說明這個 API 的運作原理、為什麼實體配置會洩露地區資訊,以及究竟是什麼限制了它作為追蹤訊號的實際效用。
核心要點
navigator.keyboard.getLayoutMap()會回傳一份從實體按鍵代碼(如"KeyQ")到該配置在該位置產生的字元的對照表(QWERTY 下是"q",AZERTY 下則是"a")。- 實體配置與地區、語言存在關聯——AZERTY 常見於法國,QWERTZ 是德國/奧地利/瑞士的標準,JIS 則是日本特有的——這提供了一個獨立於
navigator.language或時區的訊號。 - 這個 API 僅在桌面版 Chromium 系瀏覽器(Chrome 69 以上、Edge、Opera)中實作;Firefox 和 Safari 完全沒有提供這個介面,規範本身也預期行動裝置不會支援它,因此它無法涵蓋相當一部分訪客。
- 它不會向使用者顯示任何授權彈窗;唯一的限制是要求安全(HTTPS)情境,以及預設值為
self的keyboard-mapPermissions Policy 功能——那是網站自己控制的開關,與使用者同意完全是兩回事。 - 由於變更系統鍵盤配置是一個刻意且罕見的操作,與螢幕解析度或視窗大小等訊號相比,這個值在多次工作階段之間顯得異常穩定。
什麼是鍵盤配置指紋?
鍵盤配置指紋是指利用 Keyboard API 判斷作業系統目前使用的實體按鍵配置,並將該值納入裝置或瀏覽器識別碼的做法。這只是一次簡單的 API 呼叫,卻增添了其他指紋技術未曾涵蓋的訊號:大多數指紋套件讀取的是繪圖硬體(Canvas、WebGL)、已安裝的軟體(字型),或使用者回報的偏好設定(navigator.language、時區)——都不會直接反映你的實體鍵盤與作業系統之間的連接方式。
與字型或 Canvas 指紋不同,這項技術並非透過繪製差異來做推論。getLayoutMap() 是一個專門為此設計的 API,會直接回傳配置資訊,這正是它值得單獨了解的原因。
getLayoutMap() 內部:它回傳了什麼
getLayoutMap() 會回傳一個 Promise,其解析結果是一個 KeyboardLayoutMap——一個唯讀、類 Map 的物件。它的鍵是實體按鍵的代碼(code),取自標準的 code 值集合,透過按鍵在鍵盤上的位置來識別它,而不管鍵帽上印的是什麼(無論何種配置,"KeyQ" 永遠是 Tab 鍵右側的那個鍵)。它的值則是你目前配置在該位置產生的字元。
// 讀取目前配置下每個實體按鍵產生的字元
async function getKeyboardLayoutSignal() {
if (!navigator.keyboard?.getLayoutMap) return null; // Firefox、Safari:不支援
const layoutMap = await navigator.keyboard.getLayoutMap();
const probeCodes = ['KeyQ', 'KeyW', 'KeyA', 'KeyY', 'KeyZ', 'Semicolon', 'BracketLeft'];
// QWERTY 下:KeyQ -> "q"。AZERTY 下:KeyQ -> "a",KeyW -> "z",KeyA -> "q"。
return probeCodes.map((code) => layoutMap.get(code) ?? null).join(',');
}
**代碼(實體位置)與鍵(產生的字元)**之間的區別,正是這項技術具有可指紋性的關鍵所在:兩位硬體完全相同、瀏覽器版本也完全相同的訪客,僅僅因為作業系統鍵盤配置設定不同,對同一次探測就可能回傳不同的字串。只需探測幾個在 QWERTY、AZERTY、QWERTZ 之間差異最大的代碼(字母鍵),就足以用極少的程式碼把訪客歸入某個具體的配置族群。
為什麼實體配置會洩露地區資訊
鍵盤配置的分布並不平均——它們依國家和語言社群高度聚集:
- QWERTY 在美國、英國及大多數英語地區佔主導地位(英國變體在少數標點鍵位上有所不同)。
- AZERTY 是法國和比利時的預設配置。
- QWERTZ 是德國、奧地利和瑞士的標準配置。
- JIS 配置是日本特有的;ЙЦУКЕН 則用於俄羅斯及其他使用西里爾字母的國家。
正是這種聚集性使這個訊號對追蹤者具有價值:它是一個獨立於 IP 地理定位的地區代理訊號,也獨立於 Accept-Language 標頭或 navigator.language——一位重視隱私的使用者可能早已改寫了這兩者。一位 IP 顯示為美國、navigator.language 也是英語,卻回報 AZERTY 配置的訪客,這一矛盾值得被標記出來——這正是我們的指紋一致性自我檢查清單在 GPU/作業系統不匹配情境中所採用的邏輯,同樣適用於語言與配置的不匹配。單獨來看,配置是一個粗粒度、低資訊熵的訊號——全球常見配置只有數十種——但與其他屬性組合起來,它能進一步縮小訪客所能融入的人群範圍。
瀏覽器支援與授權模式
這項技術的實際涵蓋範圍,取決於這個 API 實際存在的瀏覽器:
| 瀏覽器 | getLayoutMap() 支援情況 | 面向使用者的彈窗 |
|---|---|---|
| Chrome / Edge / Opera 桌面版(Chromium) | 支援(Chrome 69 以上) | 無 |
| 行動版 Chromium | 通常不存在,或回傳空的對照表 | 不適用 |
| Firefox | 未實作 | 不適用 |
| Safari | 未實作 | 不適用 |
有三點值得留意。第一,這是一個 Chromium 獨有的訊號——依賴它的追蹤指令碼在 Firefox 和 Safari 中只會靜默得到 undefined,因此它永遠只能涵蓋網路流量的一部分。
第二,在手機上它的涵蓋面更窄。WICG keyboard-map 規範明確寫道:由於行動裝置通常沒有實體鍵盤,這個 API「在行動裝置上通常不會存在或不受支援」;即使某個行動平台實作了它,回傳的配置對照表也可能不含任何項目。對以行動裝置為主的受眾而言,這幾乎排除了這項技術原本能歸類的大多數人——這也正是它始終只能充當輔助訊號、而非核心訊號的重要原因。
第三,在支援這個 API 的瀏覽器中,並不存在攝影機、麥克風或地理定位那樣的授權彈窗。這個 API 只要求安全(HTTPS)情境,以及 keyboard-map 這項 Permissions Policy 功能的許可,而它的預設允許清單是 self。這個預設值值得細看:頁面上的第一方指令碼可以自由呼叫,跨來源的 <iframe> 則會拿到 SecurityError,除非嵌入頁面以 allow="keyboard-map" 明確授權。因此它劃出的是網站營運者在自己與嵌入內容之間的界線,而不是向訪客徵求任何同意。從使用者的角度看,這次呼叫是無聲的:沒有圖示、沒有彈窗,也無從察覺它已經發生。相較之下,相關的 keyboard.lock() 方法被限定在全螢幕工作階段和遊戲情境中——這是另一個功能,有著不同的威脅模型,並非 getLayoutMap() 的門檻。這個 API 目前仍被記錄為一項實驗性、非 Baseline 的特性,這正是跨瀏覽器支援遲遲未能擴大的原因。
穩定性:為什麼這個訊號幾乎不會變化
大多數指紋屬性會隨時間漂移:接上第二台螢幕,螢幕解析度就會改變;安裝一款新字型,字型清單就會增加;瀏覽器更新可能會微調 User-Agent 字串。相較之下,鍵盤配置異常靜態。變更作業系統層級的配置需要開啟系統設定並刻意切換——絕大多數使用者從未這樣做過,因為它與他們每天使用的實體鍵盤綁在一起。這種穩定性正是它作為輔助追蹤訊號具有吸引力的原因:即便指令碼只是偶爾檢查一次,上個月記錄的值放到今天幾乎肯定依然正確,這與隱私瀏覽器可能每次工作階段都會輪替的 Canvas 雜訊截然不同。
鍵盤配置指紋在全局中的位置
與 Canvas、WebGL 或字型指紋相比,鍵盤配置指紋只是次要貢獻者,但它揭示了一個值得留意的規律:瀏覽器不斷暴露出狹窄、專用的 API(媒體裝置、語音合成音色、網路資訊),每一個都會洩露一小片裝置或地區資料,且都沒有自己的授權彈窗。它們單獨存在時都不具決定性;疊加起來,效果就會累積。我們的瀏覽器指紋完整指南介紹了這些訊號如何組合,字型指紋則是它的近親——兩者都針對 Chromium 及其他瀏覽器、都沒有任何可見的請求,也都是作為眾多輸入之一時才最具威力,而非單獨充當識別標誌。
如何緩解
- 使用未實作這個 API 的瀏覽器。 Firefox 和 Safari 根本沒有提供
getLayoutMap(),因此無論其他設定如何,使用這些瀏覽器的訪客都不會受到這項具體技術的影響。 - 在 Chromium 上,收窄指令碼能夠靜默觸及的範圍。 能在第三方追蹤指令碼執行前就將其攔下的擴充功能,可以直接消除呼叫點——但要謹慎選擇,因為一個擁有廣泛權限的擴充功能本身就是一個追蹤媒介;安裝之前請先閱讀瀏覽器擴充功能隱私風險。如果你不只是訪客、還經營著自己的網站,那麼
Permissions-Policy: keyboard-map=()可以為你的頁面及其中所有嵌入內容關閉這項功能。 - 不要只依賴隱藏語言設定。 若一邊偽裝
navigator.language或 IP 所顯示的地區,一邊卻保留真實的鍵盤配置不變,恰恰會製造出這個訊號最擅長捕捉的那種矛盾——讓所有訊號保持一致,比單獨隱藏其中一個更重要。 - 認清局限。 與字型和 Canvas 指紋一樣,這是一種繪製/API 層級的訊號,而非儲存的資料——隱私/無痕模式不會改變它,因為根本沒有什麼可清除的。
常見問題
鍵盤配置指紋需要任何授權嗎?
不需要。getLayoutMap() 只要求安全(HTTPS)情境,以及 keyboard-map 這項 Permissions Policy 功能——它預設就對頁面自身的來源開放,也可以被網站自己的標頭關閉。這兩者都不會向訪客顯示同意彈窗。它在設計上就是無聲的。
哪些瀏覽器可能被這樣指紋?
只有桌面版的 Chromium 系瀏覽器——Chrome(69 以上版本)、Edge、Opera 以及其他基於同一引擎打造的瀏覽器。Firefox 和 Safari 都沒有實作 getLayoutMap(),因此這項技術完全不適用於它們的使用者;規範也預期行動裝置同樣不會支援它。
我的鍵盤配置就等於我的輸入語言嗎?
不一定。配置是綁定在實體鍵盤上的系統層級設定;你可以用與該語言「原生」國家不同的配置來輸入某種語言,儘管大多數人不會這麼做。這正是配置作為額外訊號有用的原因,而不是 navigator.language 的重複。
僅憑這一項就能識別出我嗎?
不能——全球常見配置只有數十種,因此它單獨來看是一個低資訊熵的訊號。只有與其他指紋屬性結合,或者當它與你回報的其他地區訊號相矛盾時,它才會變得有意義。
我該如何查看自己的瀏覽器暴露了什麼?
執行 BrowserInsight 的指紋檢測工具,查看你的瀏覽器洩漏的完整訊號集合,包括你回報的各項值之間是否彼此吻合。
結語
鍵盤配置指紋是一次微小而安靜的 API 呼叫,它把一個幾乎沒人在意的系統設定,變成了一個與地區相關、無需授權的訊號——而且由於變更它需要刻意的操作,這個訊號異常穩定。它侷限於桌面版 Chromium 瀏覽器,單獨來看資訊熵也不高,但它清楚展示了一種模式:狹窄、專用的瀏覽器 API 不斷累積——每一個都洩露一點點,沒有一個會發出詢問,而它們合在一起,就會縮小一位訪客所能融入的人群範圍。
想看到的是整套訊號,而不只是單獨一項?執行免費的指紋檢測工具——它會讀取你的瀏覽器對外交出的各項屬性,並告訴你哪些彼此吻合、哪些互相矛盾。
推薦閱讀:


