字体指纹通过你设备上已安装的字体来识别你。了解字体枚举与测量的原理,以及如何减少你暴露的信息。
字体指纹通过你设备上已安装的字体集合来识别它。你所拥有的那套特定字体组合——由你的操作系统、已安装软件、语言包和设计工具共同塑造——出人意料地具有区分度,而网站无需任何特殊授权,仅凭 JavaScript 就能探测出这种组合。本文将解释字体枚举与测量的工作原理、为什么结果如此具有识别性,以及如何减少你泄露的信息。
核心要点
- 并没有无需授权就能列出你字体的 API;追踪者通过渲染文本,并测量当所请求的字体缺失时浏览器如何替换为后备字体来推断它们。
- 几百次「存在/不存在」的探测组合起来,便构成一份高信息熵的画像,因为字体集合反映了你的操作系统、已安装应用和语言包。
- 该技术无需任何授权,且能在清除 Cookie 或使用隐私模式后依然存续,因为它源自渲染行为,而非存储的数据。
- 较新的
queryLocalFonts()API 虽能列出真实字体,却被一个明确的授权弹窗所限制,因此并非追踪者所依赖的隐蔽手段。 - 最好的防御是趋同:像 Tor 浏览器这样的浏览器会将可见字体集合标准化,让所有人看起来都一样,而不是添加自定义字体。
什么是字体指纹?
字体指纹是指确定你系统上安装了哪些字体,并把这份列表作为设备标识符一部分的做法。你的字体集合很少会被你留意,却携带着实实在在的信息熵:一台默认安装的 Windows、一台装了 Adobe 应用的 Mac,以及一台带开发工具的 Linux,所携带的字体集合明显各不相同。
网站不能直接问「你有哪些字体?」——并没有一个能返回完整列表的直接 API。于是它们通过测试特定字体、观察文本如何渲染来推断这份列表。与 Canvas 和音频指纹一样,它依赖的是可测量的渲染差异,而非存储的数据,因此能在清除 Cookie 后依然存续。诸如 EFF 的 Cover Your Tracks 这类隐私研究工具,正展示了这些被动信号在组合之后是多么具有区分度。
字体检测如何工作
经典技术是后备测量。当浏览器被要求用一种未安装的字体渲染文本时,它会悄悄替换成一种默认字体——而替换字体的字母尺寸几乎总会有所不同。通过测量这些尺寸,脚本就能判断所请求的字体是否真的存在。
方法如下:
- 测量基准。 用已知的通用字体(
monospace、sans-serif、serif)渲染一段测试字符串,并记录其精确的宽度和高度。 - 请求候选字体。 用同一段字符串,请求一种特定字体(例如「Calibri」)并带上通用后备。
- 比较。 如果尺寸与后备基准不同,则该候选字体已安装;如果与后备一致,则未安装。
- 重复。 遍历一份包含数百种常见字体的列表,构建一份「存在/不存在」的画像。
// 通过后备测量检测某种特定字体是否已安装
function isFontInstalled(font) {
const baseFont = 'monospace';
const text = 'mmmmmmmmmmlli';
const span = document.createElement('span');
span.style.fontSize = '72px';
span.style.position = 'absolute';
span.style.left = '-9999px';
span.textContent = text;
document.body.appendChild(span);
span.style.fontFamily = baseFont;
const baseWidth = span.offsetWidth;
// 请求候选字体,以基准字体作为后备
span.style.fontFamily = `'${font}', ${baseFont}`;
const testWidth = span.offsetWidth;
document.body.removeChild(span);
return testWidth !== baseWidth; // 宽度不同 => 该字体存在
}
现代的变体会用 Canvas API 更精确地测量文本。脚本不再读取元素的盒子尺寸,而是用 measureText() 把测试字符串绘制到画布上,并检查返回的字形度量——即便两种字体恰好拥有相同的步进宽度,这些度量也可能不同,从而让检测更可靠、更难被粗糙的防御所欺骗。
枚举与 Local Font Access API 的区别
把两种截然不同的能力区分开会有所帮助:
| 方式 | 授权 | 揭示的内容 |
|---|---|---|
| 后备/画布测量 | 无需 | 你专门探测的每一种字体的存在或不存在,一次一种 |
queryLocalFonts() | 明确弹窗 | 已安装字体的完整列表,包括 PostScript 名称与字体族名称 |
第一种方式只能就脚本已想到要测试的字体回答「这种字体安装了吗?」,因此追踪者会附带一份精心整理的、包含数百种常见字体族的列表。第二种——即 Chromium 系浏览器中提供的 Local Font Access API——只需一次调用即可返回完整的本地字体集合,但前提是用户授予了 local-fonts 权限。由于该弹窗醒目且容易被拒绝,隐蔽的追踪者很少使用它;无声的后备测量方法仍是字体指纹的主力手段。
为什么你的字体列表如此具有识别性
字体集合从多种来源累积而来,而正是这种多样性使它们对追踪格外有用:
- 操作系统及其版本自带不同的默认字体。
- 已安装的应用程序——Microsoft Office、Adobe Creative Cloud 和各类设计工具——会添加独特的字体族。
- 语言和地区包会添加许多系统所缺乏的字体(CJK、西里尔、阿拉伯文等)。
- 手动安装的字体来自设计师和开发者,高度因人而异。
每一个「装了还是没装」的答案都是一比特信息熵,而几百次探测组合起来,便能形成一份足以从众多用户中锁定许多人的画像——尤其是在与其他信号结合之时。
字体指纹在全局中的位置
字体指纹很少单独发挥作用。它是一整套信号栈中的一层,这套栈还包括 Canvas、WebGL、音频、屏幕参数和 User-Agent 数据。它还与浏览器插件存在重叠:某些扩展会注入自己的字体或改动字体行为,反而可能让你更易被识别,而非更难。理解整体画面,正是我们浏览器指纹完整指南的意义所在。
如何减少你的字体指纹
- 使用限制字体枚举的浏览器。 Tor 浏览器将网站可见的字体限制为一套标准集合,让所有人看起来都一样;Firefox 的抗指纹模式也有类似做法。Brave 则采用随机化。
- 避免安装不寻常的字体,尤其是在你用于隐私敏感浏览的浏览器配置中——你的字体集越罕见,你就越独一无二。
- 警惕注入字体的扩展,它们会扩大你可被检测到的字体列表。
- 认清局限。 与其他技术一样,隐私/无痕模式并不能阻止字体指纹;它只清除 Cookie 和历史记录。
一个反直觉的教训是:趋同才能保护你——目标是让自己看起来和大家一样,而不是添加更多自定义字体。
最强的防御实际如何运作
最有效的工具与其说是隐藏你的字体,不如说是抹平用户之间的差异:
- 标准化。 Tor 浏览器附带一套固定的字体包,并告诉网页只存在这些字体,无论实际安装了什么。因此每一位 Tor 用户都报告相同的字体集合,把这一信号的信息熵压缩到接近于零。Firefox 的
privacy.resistFingerprinting模式秉持同样的理念。 - 随机化。 Brave 会对测量值施加轻微且按会话变化的扰动,使得重复探测无法返回稳定的答案。这会破坏使指纹有用的跨访问可关联性,即便单次快照看起来仍然合理。
- 缩减你的暴露面。 在加固型浏览器之外,请为隐私敏感的浏览保留一个干净的配置:避免安装罕见的字体族,并移除注入字体的扩展。一台原装的操作系统远不如一台装满设计软件的系统那样独特。
没有任何单一开关能消除字体指纹,把加固型浏览器与精简的字体集合结合起来才是现实的目标。至于更宏观的威胁模型——字体如何与其他信号组合——W3C 在其 Web 平台指南中将指纹技术记载为一项公认的隐私关切。
常见问题
网站能直接看到我所有已安装的字体吗?
未经授权则不能。并没有一个无需授权就能列出每一种字体的 API。网站通过渲染文本并测量后备替换来推断你的字体,这会一次一种地揭示某字体的存在与否。较新的 queryLocalFonts() API 虽然能列出字体,但需要明确的授权弹窗。
字体指纹需要任何授权吗?
不需要。后备测量方法使用的是普通的文本渲染和元素尺寸测量,既不需要授权,也不会触发弹窗。这正是它对追踪者颇具吸引力、又难以被用户察觉的原因。
清除 Cookie 能去掉我的字体指纹吗?
不能。该指纹是从你安装了哪些字体中推导出来的,而非来自存储的数据,因此清除 Cookie 或使用隐私模式都不会改变它。要削弱它,需要在浏览器层面限制字体枚举。
我如何查看自己的浏览器暴露了什么?
运行 BrowserInsight 的指纹检测工具,查看你的浏览器泄漏了哪些信号;再运行插件与扩展检测工具,审查那些可能影响你字体画像的插件。
结语
字体指纹读取你系统累积下来的那套独特字体集合,并把它变成一个稳定的标识符——无需授权、无需麦克风、无需存储的数据。由于字体集反映了你的操作系统、应用程序和使用习惯,它们携带着实实在在的信息熵,在 Canvas、WebGL 和音频信号的衬托下尤为如此。有效的防御不是添加更多字体,而是融入人群:使用一个将字体枚举标准化或随机化的浏览器,并让你的隐私配置保持普通。
推荐阅读: