你的字体列表是花了好几年才攒起来的。操作系统先放进去几百款,Office 添上了自己的一批,Adobe 每装一个应用又加一些,还有你 2021 年为某个项目装的那款展示字体,从此再也没离开过。任何网站都能在几百毫秒内读出这一切,不需要任何权限弹窗;而这份列表长得越大,别人那份与它完全一致的可能性就越小。
浏览器从来没有给过网站直接索要字体列表的途径,恰恰因为它太能识别人了。结果这根本不构成障碍。脚本不必开口问,它可以去测量:需要的不过是一行看不见的文字和两次宽度读数。本页上的小工具就对 175 款候选字体跑了一遍这种测量,让你看到你的浏览器交出了什么,以及在所有跑过这次扫描的人当中,你这套字体组合有多常见。
字体指纹如何运作:量宽度的把戏
整套技术全在这里了。脚本创建一个不可见的 <span>,塞进一串无意义的字符 mmmmmmmmmmlli,字号 72 像素,然后问浏览器:用 monospace 这样的通用字族渲染出来有多宽。接着它把字族换成 "Segoe UI", monospace,再读一次宽度。如果 Segoe UI 装了,浏览器就用它来渲染,宽度随之变化;如果没装,回退字体顶上,数字纹丝不动。一款候选字体,两次宽度读数,全程没有任何 API 出场。
这串字符也不是随手打的:在大多数字体里,m 是最宽的小写字母,l 是最窄的,所以换一款字体,总宽度的变化会被拉到最大。在你自己的机器上亲眼看看:
font-family: monospace …font-family: "Segoe UI", monospace …拿到同一个答案还有更利落的路子。document.fonts.check() 一次调用就把结果作为布尔值递回来——在 Chromium 系浏览器里是这样,这得归功于一个违反规范的怪癖;Firefox 和 Safari 则不管你报什么字体名都回答 true——而在 canvas 上测量文字宽度,则连 DOM 都不用碰。这个领域里,Web 平台倒是恰好有一个讲礼貌的 API:queryLocalFonts(),它会返回真实的字体名,甚至字体文件本身——前提是用户先在权限弹窗里点了同意。没有哪个追踪器给你弹过那个窗。
这一整族技术统称字体枚举(font enumeration),而追踪器并不是它唯一的客户。反欺诈和机器人检测系统也在跑同样的探测,用来核实一台设备是不是它自称的那台。正因为如此,这个结果会跟着你进入区区广告商永远到不了的地方:支付流程、登录风险评分、账号审查。
你安装的字体暴露了什么
让这份列表值钱的不是它有多长,而是每一条记录各自招认了什么。字体来到你的机器上,都是你安装其他东西时捎带的副产品。Segoe UI 意味着 Windows。Helvetica Neue 意味着 Apple。DejaVu Sans 意味着 Linux。Bahnschrift 把 Windows 收窄到 10 或更新,Segoe UI Variable 则直接说明是 11。Minion Pro 或 Myriad Pro 意味着这台机器上装过 Adobe 的软件。而如果你的 Windows 装的是中文环境——你现在用的多半正是——它就自带 SimHei(黑体)和 DengXian(等线)这些字体,一台美国装出来的机器没有任何理由拥有它们。你的字体就是一份档案,记录着你在用什么软件,以及大致是什么时候装的机:
Segoe UI Windows 自带Helvetica Neue macOS 和 iOS 自带DejaVu Sans Linux 桌面发行版自带Bahnschrift Windows 10 新增,此前没有Segoe UI Variable Windows 11 新增Minion Pro 由 Adobe 应用安装Myriad Pro 由 Adobe 应用安装这和我们在 一个浏览器,五种操作系统声明 一文里讲过的是同一类泄露:浏览器上报的标签可以随便改,标签背后的机器却始终按本性行事。一个伪造出来的 macOS User-Agent,底下坐着一份满是 Segoe 和 Calibri 的字体列表,这种自相矛盾任何脚本都能在几毫秒内看穿。
在 EFF 的 Panopticlick 研究 里,字体列表——当年是通过 Flash 和 Java 收集的——携带了 13.9 比特的识别信息,在所有被测量的浏览器指纹信号里仅次于插件列表。此后浏览器把插件阉割了——如今 navigator.plugins 在哪台机器上都报同一份短短的 PDF 查看器列表(或者干脆什么都不报)——而字体的可测量程度和 2010 年一模一样,分毫未减。这份列表还会泄露两次:一次是作为列表本身,另一次是通过 canvas 指纹——渲染器手头有哪些字体可用,参与决定了一次 canvas 绘制产出的每一个像素。
而且这份列表极有耐心。清空 cookie 它不变,开无痕窗口它不变,你的 VPN 它更是全然不放在眼里。它只在你安装或卸载软件时才会变,对多数人来说一年也就那么几次。以追踪标识符的标准衡量,这算得上超长的保质期。
字体指纹防得住吗?拦截的陷阱
市面上有些扩展承诺挡下这种测量,要么拦截探测,要么往里灌噪声。问题出在本站到处用来抓半吊子伪装的那条老规律上:防御手段本身,比它要藏的东西还罕见。
一个跑在 Windows 上的桌面版 Chrome,报出来的却只有寥寥几款 web 安全字体,这不叫匿名,这叫古怪,而古怪正是指纹识别赖以为生的东西。随机化派更糟。真实的字体列表能几个月纹丝不动,所以一份每次访问都在变异的列表,恰好把你标成了装着那款扩展的极少数人之一。何况伪造数字的扩展,靠的是覆盖页面用来测量的那几个函数。这又制造出第二个问题:被覆盖过的内建函数在被检视时不再报告 [native code],而这正是出卖 隐身插件 的同一个破绽。
认真防御这件事的浏览器,都是整个儿地防。Tor Browser 自带一套字体,并且只允许网站使用这份固定列表,于是每个 Tor 用户呈现的都是大致相同的一小套。这确实有效,同时也向每个网站宣告你在用 Tor。Brave 的做法是“farbling”(随机扰动):按站点、按会话,它展示给追踪器的是你的系统字体,外加从你已装字体里随机抽出的一部分,于是追踪器今天采到的列表会和明天的悄悄对不上,却从头到尾不显得不可能。两条路都行得通,原因是整个浏览器都是围绕这套防御建起来的,而不是一块补丁在和它所寄身的浏览器较劲。
到底该怎么做
- 先看看你自己的列表。 本页上的小工具已经把你的浏览器量完了。展开字体列表,看看任何网站都能看到的东西,再查查在本站跑过的全部扫描里,你这套字体组合有多常见。如果它是独一份的,最好趁事情还没找上门就心里有数。
- 别东一块西一块地打补丁。 在一个其余一切正常的浏览器上装一个字体拦截扩展,只会让你更罕见,不会让你更安全。如果你希望日常浏览就有这层防御,就用一个围绕这套防御设计出来的浏览器——Brave 或 Tor——并且明明白白地接受它们各自的代价。
- 如果你运营着多个身份,字体列表就是身份的一部分。 每个配置文件的字体都得和它声称的操作系统对得上,而且每一次会话都得如此。Incogniton 给每个配置文件配一份属于它自己的字体列表,并让它在一次次会话之间保持稳定,这样网站读到的那份档案属于配置文件,而不属于你的机器。
自打你有这台机器起,你的字体就一直在替你回答关于你的问题。上面的小工具展示了它们一直在说什么,而 完整浏览器扫描 会告诉你,浏览器的其余部分还在旁边补充了些什么。