键盘布局指纹通过 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 在不断累积——每一个都泄露一点点,没有一个会发出询问,而它们合在一起,就会缩小一位访客所能融入的人群范围。
想看到的是整套信号,而不只是单独一项?运行免费的指纹检测工具——它会读取你的浏览器对外交出的各项属性,并告诉你哪些彼此吻合、哪些相互矛盾。
推荐阅读:


