什么是字体指纹?跨平台文本一致性
什么是字体指纹?了解字体可用性、回退行为和文本指标如何暴露平台特征并影响浏览器隐私。
BotBrowser Team
BotBrowser 支持让字体行为与选定 profile 保持一致,并帮助团队比较不同主机上的文本结果,但不保证匿名或被网站接受。因此本文把字体行为作为可测量的隐私和兼容性边界,而不是身份结论。CSS 媒体查询提供了相关的显示信号,详见它们如何暴露配置。
TL;DR
字体可用性、回退行为和文本指标会暴露平台特征。让选定配置保持权威,在不同主机上测试覆盖多语言的代表性页面,并把可见漂移视为兼容性信号,而不是身份证明。
Contents
字体指纹测量什么
字体指纹利用字体可用性、回退行为和文本指标,来了解浏览器如何渲染文字。网站不需要任何特殊权限,就能观察文字的排版更像 Windows、macOS、Linux 还是移动设备。它读取的是普通的渲染行为,并且可以与 canvas、WebGL、语言和屏幕信号组合使用。
- 字体可用性会透露操作系统和语言包的大致特征。
- 回退行为显示当请求的字体族或字形缺失时,浏览器会选用哪种字体。
- 文本指标显示浏览器如何测量字形宽度、行高和排版。
- canvas 文本可以通过渲染出的像素反映同样的字体行为。
页面可以用三种普通方式观察字体。它可以请求用某个命名字体族绘制文字,并查看实际使用的是该字体族还是替代字体。它可以依赖 CSS Fonts Level 4 中的回退规则,这些规则描述了浏览器如何遍历 font-family 列表,并为第一选择无法绘制的字符挑选另一种字体。它还可以调用 measureText 之类的测量方法,返回字符串的宽度和边界框。选用不同的字体时,同一个字符串的测量结果也不同。
页面并不需要直接拿到已安装字体的列表,这些信号就已经有意义。盒子宽度、换行位置和滚动高度都来自所用的字体,所以仅凭排版就带有平台信息。canvas 文本提供了同一行为的第二个视角,因为文本绘制调用产生的像素取决于所选字体和光栅化器。
字体还会与语言和地区相互作用。当页面声明了语言,或浏览器报告了首选语言时,中文、日文和韩文等文字的回退顺序会遵循这些提示。如果配置报告的是一种语言,却用另一种语言的字体选择来绘制文字,细心的观察者不用读取任何字体名称就能发现这种不匹配。请把配置的字体清单和语言设置当作一个决定,而不是两个。
为什么一致性比任何单个数值更重要
同时比较 profileTarget、hostClass 和 fontMode;将 browserVersion 与文字度量记录一起保存。
对隐私团队来说,风险很少来自某一个孤立的数值。风险来自不一致。浏览器配置可能声称自己是 Windows 桌面,而宿主机却表现出 Linux 风格的文本行为。移动身份可能带有桌面字体特征。配置可能在一个环境里保持稳定,在另一个环境里发生漂移。
三种一致性很重要:
- 平台身份:目标为 Windows、macOS、Linux 和移动平台的配置,不应暴露宿主机特有的字体特征。
- 会话连续性:同一个配置应在不同启动和不同机器上产生稳定的文本行为。
- 跨信号一致性:字体行为应与配置的浏览器品牌、设备类别、语言和渲染模型相符。
好的字体处理并不是阻止排版。页面仍然需要正确显示文字,用户仍然需要可读的控件。目标是让文本行为来自所选配置,而不是来自宿主机碰巧安装了什么,同时保持正常的网页兼容性。canvas 文本是这个话题最近的邻居,canvas 一致性说明了同样的原则如何应用于绘制出的像素。
这对服务器部署尤其重要。许多生产集群运行在 Linux 上,因为它高效且易于扩展,而团队需要的浏览器身份可能代表桌面或移动环境。字体是宿主基础设施最容易暴露出来的地方之一,因此值得做一次明确的检查。
同样的比较也适用于无头和有界面运行,但不同宿主机、显示服务、图形后端或字体栈之间并不保证得到完全相同的渲染结果。可以把相同配置和参数作为受控输入,同时记录宿主机和显示策略,然后比较实际得到的指标,不要假定定时无头截图一定会与有界面会话一致。如果出现差异,应先检查字体模式和渲染环境,再判断是否属于产品回归。
让文字在不同宿主机上保持一致
配置模式与内置字体包
在配置、宿主机、授权和字体模式均受支持时,配置字体可以让字体可用性和文本指标与目标平台对齐。应核验具体配置、宿主机、授权和字体模式;并不保证在所有渲染界面上完全一致。Linux/Ubuntu 可移植性需要 ENT Tier1;Android 字体支持属于 PRO。
回退仍可能改变最终渲染结果。在某些平台上,罕见 CJK 字符可能会回退到系统字体;不同宿主机和文本界面的覆盖范围也不完全相同。
何时有意使用 expand 或 real
字体行为通过 --bot-fonts 选项控制,它有三种模式:
profile是默认模式,使用配置内嵌的字体。expand使用配置字体,并增加宿主机字体回退。real使用真实的系统字体,不提供字体保护。
当跨宿主一致性很重要时,请保持 --bot-fonts=profile,这样配置的字体清单和回退策略仍然是权威依据。只有当工作流有意允许宿主机差异,并且这种差异属于测试计划的一部分时,才使用 expand 或 real。如果测试中的字体列表显示出宿主机字体,请先检查模式,再去别处排查。
CJK 与混合文字页面
CJK 文本高度依赖语言包、回退顺序和渲染行为,所以它是发现宿主机泄漏最快的方式。字形缺失通常意味着可用字体清单里没有合适的字体,BotBrowser 文档也提到 Windows 和 macOS 配置默认包含 CJK 支持。关于中文、日文和韩文文本的细节,请阅读 CJK 字体渲染。关于让同一个配置在不同操作系统上保持稳定这一更大的问题,请参阅配置文档。
建立有代表性的文字集合
比较前为每个检查点指定 pageId、viewport、zoomPercent、profilePackage 和 applicationRevision。
有用的文字集合来自被测试的产品。从客户每天使用的页面开始:登录、账户设置、搜索结果、表格、编辑器、结账、可打印文档和支持内容。把会改变排版的状态也包括进来,例如校验消息、禁用的控件、展开的菜单和空结果。一段示例文字无法代表一个应用的排版。
让集合小到每次发布都能运行。十个精心挑选的页面,通常比数百张无人查看的截图更有用。每个页面都应有负责人、纳入的理由和稳定的检查点。检查点可以是截图、文档导出,或记录下来的功能结果,例如按钮标签保持可见。
语言覆盖应跟随真实用户。支持英语、西班牙语、法语、俄语和中文的产品,需要在每种受支持的语言中准备有代表性的文字。如果日语、韩语、阿拉伯语、希伯来语或其他文字是产品的一部分,也要加入。包含混合语言的记录,因为姓名、地址或目录条目经常使用不止一种文字。
选择能够检验正常排版又不显得刻意的内容。较长的客户姓名、狭窄的表格列、多行标签、日期、货币和文档预览都是很好的候选,因为它们能暴露实际的排版回归。仅为枚举环境细节而创建的页面无法说明产品是否仍然可用,所以不要放进来。
为每个视觉检查点记录预期的视口和缩放级别,因为不同视口造成的换行并不是字体回归。发布记录还应标明配置系列、BotBrowser 版本、基础镜像和应用修订版本。这些信息让另一位工程师可以重现同样面向用户的检查,而无需收集不必要的机器数据。
自带网页字体的页面还需要一条规则:只在字体加载完成之后再记录检查点。从临时字体切换到最终字体会改变宽度和换行,在切换中途拍下的截图看起来像回归,其实只是时间差异。比较结果之前应先等待文字稳定。
文本检查应包含用户操作之后才出现的状态。展开的菜单、校验消息、自动补全结果、模态对话框和加载状态,其排版限制往往比初始页面更紧。用键盘遍历同样的状态,因为焦点指示、辅助文字和错误汇总可能使用与普通内容不同的字体排印。
当产品依赖可打印页面、导出文档和无障碍视图时,也要把它们包括进来。确认分页、标题、表单标签和阅读顺序仍然可用。把应用结果与视觉结果一起记录,让审阅者能分辨无害的外观变化和信息丢失。
跨宿主机验证并批准结果
跨宿主机比较
每一组比较记录 baselineId、candidateHost、renderingBackend 以及关联已审页面的 visualDecision。
跨宿主机审查回答一个简单的问题:同一个已批准的配置,在每一类受支持的 worker 上,是否保持了同样可用的文本体验?在生产使用的宿主机上运行这套代表性集合,并把结果与已批准的基线比较。
- 在两种不同的宿主操作系统上运行同一个配置。
- 比较一个有代表性的页面,其中包含普通文本、需要时的 CJK 文本,以及工作流涉及时的 canvas 文本。
- 确认观察到的行为与配置目标一致,而不是与宿主机一致。
- 把结果保存为发布或配置验证资料的一部分。
微小的像素差异并不自动意味着发布不合适。验收规则应来自产品本身。截图服务可能需要比只读取和提交表单的工作流更严格的视觉基线。在审查前定好容差,让发布决定保持一致,不取决于恰好由谁来检查结果。
把缺失字符、不可读的控件、意外的符号替换,以及与周围内容重叠的文字视为阻塞问题。当没有信息丢失且页面保持稳定时,换行位置的变化可能是可以接受的。与页面负责人一起记录这项决定,避免下一次发布时再次争论同一个变化。
截图是有用的证据,但不是唯一的证据。文本选择、表单输入、复制粘贴、PDF 输出和无障碍标签,可以暴露静态图像看不到的、客户能感知到的问题。为每个代表性页面和配置系列指定负责人。负责人决定可见差异是否符合预期,在产品排版变化时更新基线,并记录发布决定。
发布记录与更新
浏览器版本、配置包和服务器镜像构成一个可部署的整体。把它们的版本一起记录在部署记录中。针对某个版本批准的配置,不应在浏览器或镜像发生重大变化后被悄悄重用。重新运行代表性文字集合,并记录新的基线。
尽可能一次只更新一层。先用现有已批准的配置和镜像验证新的 BotBrowser 版本。然后验证计划中的配置更新。最后验证基础镜像或语言包的任何变化。这个顺序能缩小视觉变化的来源,并让回滚决定更直接。添加或移除语言包,或者更新桌面库,都可能改变普通文本,所以新镜像在进入 worker 池之前要通过同样的检查。
简洁的发布记录可以每个页面一行:结果、预期结果、浏览器和配置版本、宿主类别以及审阅者的决定。注明页面是通过、需要经批准的基线更新,还是需要调查。把这一行链接到截图或文档输出,以及应用负责人。避免保存无关的账户数据或完整的浏览历史,并在可行时使用专用的测试账户和合成文档。
当基线是有意改变的,把之前的结果保留足够长的时间来解释这次转变,并附上简短理由,例如产品排版更新或配置系列更新。不要把不相关的配置系列混在同一个基线里,因为桌面、移动和平板的布局有不同的预期。保留期限应遵循组织常规的质量与隐私政策,个人信息在截图进入长期记录之前应先做遮盖。
处理可见漂移
当文字意外变化时,暂停大范围推广,并用记录的配置、浏览器版本、镜像、视口和应用修订版本重现受影响的页面。在更改配置之前,先在干净的 worker 上确认这个变化。这样可以把持续存在的发布问题与损坏的缓存或某一台不健康的宿主机区分开来。
接着,把最后一个已批准的组合与候选组合比较,每次运行之间只改变一个组成部分。如果差异跟随基础镜像,就检查最近的软件包变化。如果跟随配置,就在负责人审查更新期间回到已批准的配置。如果跟随应用,就把结果交给页面负责人。
如果审查还使用了可选的 ClientRects 和文本矩形噪声设置(默认是关闭的),请在比较期间固定噪声种子。没有固定种子时,测量值会在会话之间变化,审阅者无法分辨预期的变化和真实的改变。固定种子后,重复的会话会得到相同的测量输出,比较才有意义。
记录对用户的影响,而不只是视觉差异。字形缺失、标签被裁切、控件无法使用以及文档分页变化,都需要立即关注,而无害的抗锯齿差异可能只需在基线中备注。修正之后,为受影响的配置系列重复完整的代表性集合,因为修复一个页面可能改变另一种语言或另一个文档的排版。分阶段推广,从小规模的 worker 组开始,只有在约定的检查点于每一类将接收该版本的 worker 上都通过之后,才推进发布。
BotBrowser 的字体处理覆盖什么,不覆盖什么
字体行为只是若干信号之一,配置的一致性取决于它最薄弱的那个表面。在更改配置、字体模式或宿主镜像之前,重新运行代表性集合,并把可见结果与配置目标比较。某个变化在一种语言里看起来很小,在另一种语言里可能很大,所以批准更新之前,要检查产品支持的每一种语言。字体文档列出了各种模式和排查步骤,CJK 文档更详细地介绍了中文、日文和韩文的渲染。
相关延伸阅读:文本测量指纹识别:亚像素追踪技术。
结论
如果目标是让不同主机上的文本行为稳定,请使用 --bot-fonts=profile。维护一组精简的多语言检查点,在每个支持的主机类别上重复比较,并在发布前调查字形缺失、标签裁切或分页变化。字体一致性改善的是可测量的兼容性边界,但不能证明匿名或网站接受。
公开来源
本文属于指南: 什么是浏览器指纹?原理、风险、信号与隐私边界