一段看不见的"文字体检"
打开一个网页时,除了你点的链接,页面脚本往往还在背地里做一件事:给你的浏览器做一次"文字体检"。它不关心你是谁,只关心你的文字长什么样——用了哪些字体、每个字母多宽多高、同样一句话画出来差了几个像素。这些细节你平时完全感觉不到,但组合起来,就成了一串能长期指认你的标识,也就是字体指纹。
和登录、Cookie 不一样,字体指纹属于"浏览器指纹"的一个维度。它不往你机器里存东西,而是读取你本来就有的特征。正因为什么都不用存,它格外难缠:你清掉 Cookie、退出账号、开一个无痕窗口,字体该是什么样还是什么样。
它到底是怎么认出你的
字体指纹主要有两条采集路径。
第一条是枚举已安装字体。脚本准备一份候选字体名单,挨个让浏览器"假装用这个字体"排一小段文字,再看实际效果——如果某个字体系统里真有,文字会按它的样子显示;没有的话,浏览器会悄悄回退到默认字体。通过比较渲染结果,脚本就能反推出你装了哪些字体。设计软件、办公套件、各类游戏和输入法都会带来一批独有字体,名单一长,区分度就非常高。
第二条更隐蔽,是用 measureText 之类的接口测量文字度量。脚本画一段文字,然后读回它的宽度、基线、字形边界这些数值。同一段文字,在不同系统、不同字体、不同渲染管线下,量出来的数字会有极细微的差别——往往精确到小数点后好几位。这些数字本身不敏感,但拼在一起同样能当指纹用,而且不需要一份长长的字体名单。
为什么每台设备都不一样
字体指纹之所以好用,是因为它天然就乱。你装的字体各不相同,这是最大的变量;系统的默认字体和界面字体又因操作系统、语言、版本而异;最后,文字渲染本身——抗锯齿方式、子像素处理、缩放比例、显卡和驱动——也会在像素层面留下痕迹。三层叠加,哪怕两个人买了同款电脑,用一阵子之后指纹也会分道扬镳。
和 Canvas 指纹是一家人
常被一起提到的 Canvas 指纹,原理上和字体指纹是近亲。Canvas 指纹让浏览器在画布上绘制图形和文字再截取像素,比对细微差异;而其中文字部分的差异,恰恰很大程度来自字体和文字渲染。换句话说,字体是 Canvas 指纹的重要"原料"之一。这也意味着:如果只在某一处做了手脚,另一处却露了馅,反而更显眼——防护必须是成套的。
风险在哪里
字体指纹的麻烦,核心在于"绕过你的控制"。它不依赖 Cookie,所以常规的清理手段对它无效;它可以跨站追踪,不同网站只要用同一套采集逻辑,就能把你在各处的浏览认成同一个人;对需要同时经营多个账号的人来说,它还可能把本应互不相干的身份关联起来——哪怕每个账号都单独登录、单独清理,只要它们跑在同一套字体环境里,就可能被判定为"同一台机器、同一个人"。
到底该怎么防
防字体指纹,难点不在"盖住",而在"盖得自然"。几条经得起推敲的思路:
- 统一通用族:把 serif、sans-serif、monospace 等几大 CSS 通用字体族映射到一致的呈现,减少因默认字体不同而暴露的差异。
- 隐藏异常字体:对那些辨识度过高、不该出现的字体不予暴露,压低字体名单的独特性。
- 度量微扰:对
measureText这类测量结果加入细微、可控的扰动,让量出来的数字不再是你设备独有的那一组。
比做法更关键的是原则:每个环境要稳定,同一环境多次访问结果一致,忽大忽小反而更可疑;各环境之间要独立,互不串味;而且不能全禁或全随机——把字体全关掉、或每次都给一组随机值,本身就是一种极不自然的信号,很容易被识别为"有人在动手脚"。真正可靠的做法,是让字体维度和其它指纹维度凑成一个讲得通、前后一致的整体。
这正是虎鲸在字体维度上的思路:对五大 CSS 通用族做重映射、隐藏指定字体、对文字度量做微扰,并让每个环境各自独立、自身自洽、保持稳定,同时和其它维度做自洽校验。具体行为仍以官方说明为准。
小结
字体指纹不靠存储、难以察觉,却能跨站、绕过 Cookie、甚至把多个账号关联到一起。理解它的两条采集路径和背后的成因,才能明白为什么防护不是简单地"关掉"或"随机化",而是一套讲究稳定、独立与自洽的系统工程。