返回博客
指纹

使用 BotBrowser 的字体指纹保护

字体枚举与文本度量如何产生唯一指纹,以及 BotBrowser 如何在引擎层控制字体列表与渲染。

介绍

每个操作系统随系统带有不同的字体集合,用户还会因工作或偏好安装额外字体。安装字体的组合会形成意想不到的唯一标识。网站可以通过测量特定字号下文本的渲染情况来探测哪些字体可用,得到的字体列表即可构成字体指纹。除了字体列表,字形宽度、高度与边界框等文本度量在不同平台与字体渲染引擎间也存在差异。字体枚举与文本度量共同为追踪系统提供了持久且高熵的信号。本篇解释字体指纹的工作原理以及 BotBrowser 如何在引擎层控制字体身份。

隐私影响

字体指纹已成为可靠的追踪向量多年。研究显示,已安装字体集合是高熵的浏览器属性之一,能显著缩小候选集。不同操作系统及其版本自带不同默认字体(Windows 10/11、macOS Ventura/Sonoma 等差异),企业环境中的专有字体或设计师安装的字体都会进一步增加唯一性。

因此,字体指纹往往能揭示操作系统版本、组织或专业软件生态的信息,成为强识别特征。

技术背景

字体指纹主要依赖两类技术:测量式字体枚举与文本度量。

通过测量进行字体枚举

浏览器没有直接列出已安装字体的标准 API(过时的 document.fonts.check() 覆盖有限)。因此,追踪脚本采用测量技巧:

  1. 创建一个隐藏元素并设置已知回退字体(如 monospace、serif 或 sans-serif),测量其宽高。
  2. 将 font-family 改为候选字体 + 回退,重新测量。
  3. 若尺寸变化,则表示候选字体已安装。

对数百个字体名进行检测后,脚本能构建一个二进制向量(已安装/未安装),即字体指纹。

文本度量指纹

除了字体列表外,文本渲染的精确测量(如 Canvas 的 measureText()、元素的 getBoundingClientRect())会因渲染引擎(Windows 的 DirectWrite、macOS 的 CoreText、Linux 的 FreeType)、hinting 设置与子像素渲染差异而不同,成为额外的指纹来源。

这些度量与字体列表结合,构成了高熵的复合指纹。

常见防护方法与局限

阻止字体枚举不可行,因为相关技术使用标准 CSS 与 DOM 测量 API,阻断 getBoundingClientRect() 会破坏页面布局。

安装更多字体会适得其反:字体数量越多,指纹通常越独特。

声称保护字体指纹的浏览器扩展往往通过注入 CSS 或拦截测量 API 实现,脆弱且容易被交叉检测到(例如扩展可能覆盖 measureText() 却未处理 getClientRects())。

随机化文本度量会破坏网站功能,导致布局问题。

使用极简字体集(例如 Tor Browser)虽能降低熵,但会形成特色化配置,自身容易被识别。

BotBrowser 的引擎级方法

BotBrowser 在 Chromium 引擎层通过两个机制控制字体指纹:受控字体列表与文本渲染一致性。

受控字体列表

加载配置文件时,BotBrowser 配置字体子系统以报告与目标设备一致的字体集合。这不是 CSS 覆盖或 JS 拦截,而是浏览器内部的字体枚举返回配置文件中的字体列表。

覆盖范围包括系统字体枚举结果、CSS font-family 的解析顺序、document.fonts 的响应以及字体回退链行为。

文本度量一致性

配置文件包含目标平台的文本度量数据,任何 API 的测量结果都会与配置文件匹配:

  • measureText() 返回与目标平台形状与 hinting 相符的度量值;
  • getBoundingClientRect() / getClientRects() 的值与目标渲染引擎一致;
  • Canvas 文本渲染产生与目标平台匹配的像素输出;
  • 可选的客户端矩形噪声(通过 --bot-config-noise-client-rects / --bot-config-noise-text-rects--bot-noise-seed 控制)提供确定性可重现的扰动。

跨平台字体仿真

BotBrowser 能在 Linux 上加载 Windows 配置文件并呈现 Windows 风格的字体列表与文本度量,无需在宿主系统上安装 Windows 字体文件,因为控制发生在渲染引擎层,而非通过安装字体文件实现。

配置与使用

chrome --bot-profile="/path/to/profile.enc" \
       --user-data-dir="$(mktemp -d)"

字体相关配置:

# 使用配置文件字体(默认,推荐)
chrome --bot-profile="/path/to/profile.enc" \
       --bot-config-fonts=profile

# 配置文件字体 + 系统回退字体
chrome --bot-profile="/path/to/profile.enc" \
       --bot-config-fonts=expand

# 使用真实系统字体(不启用字体保护)
chrome --bot-profile="/path/to/profile.enc" \
       --bot-config-fonts=real

控制文本度量噪声示例:

chrome --bot-profile="/path/to/profile.enc" \
       --bot-config-noise-client-rects=true \
       --bot-config-noise-text-rects=true \
       --bot-noise-seed=42

Playwright 与 Puppeteer 示例保持不变(请使用 BotBrowser 的可执行路径与相应 flags)。

验证

验证项包括:字体列表检测应与目标配置文件一致;使用 measureText() 的度量在相同配置与噪声种子下应匹配;跨 API(CSS 测量、Canvas、getClientRects)结果应一致。

最佳实践

  • 优先使用 --bot-config-fonts=profile(默认)。
  • 将字体保护与 canvas 噪声结合使用。
  • 使用针对目标区域的配置文件(尤其是 CJK 与 RTL 场景)。
  • 在多个字体探测列表上测试保护效果。

常见问题

Q: 指纹脚本通常探测多少字体? A: 常见库探测 50–500 个字体,全面脚本可超过 1000 个。

Q: BotBrowser 是否在配置文件中嵌入字体文件? A: 配置文件包含控制枚举与度量所需的数据,无需实际安装字体文件在宿主系统上。

总结

字体指纹结合字体列表与文本度量形成高熵追踪信号。BotBrowser 在引擎层控制这些信号,凭借 --bot-config-fonts=profile--bot-noise-seed 提供稳定、一致且真实的字体指纹保护。

title: "BotBrowser 字体指纹保护" description: "BotBrowser 如何在浏览器引擎层面控制字体列表和文本度量,实现跨平台一致的字体信号。" date: "2025-04-28" locale: zh category: fingerprint tags: ["fonts", "fingerprinting", "privacy", "text-metrics", "tracking"] published: true

隐私风险

已安装的字体和文本渲染度量构成追踪信号。可用字体的组合因操作系统、已安装应用和语言包而异。文本测量 API 也因字体渲染引擎差异产生平台特定的结果。

BotBrowser 的解决方案

BotBrowser 在浏览器引擎层面控制字体相关信号,而非通过 JavaScript 注入或 API 阻止。

平台一致的字体列表

当配置文件指定特定平台时,BotBrowser 报告适当的字体,不受宿主操作系统影响:

chrome --bot-profile="/path/to/windows-profile.enc" \
       --user-data-dir="$(mktemp -d)"

字体枚举查询返回目标平台的正确字体集。

字体度量对齐

文本测量 API 返回与目标平台字体渲染引擎一致的度量:

  • measureText() 结果
  • 特定字体的元素偏移尺寸
  • 字体边界框测量

无需阻止

与阻止字体访问的扩展(这本身可被检测)不同,BotBrowser 允许正常的字体 API 使用,同时控制结果。网站正常运作,但信号与配置文件匹配。

验证

加载配置文件后,验证字体一致性:

const { chromium } = require('playwright-core');

const browser = await chromium.launch({
  executablePath: '/path/to/botbrowser/chrome',
  args: [
    '--bot-profile=/path/to/windows-profile.enc',
  ],
  headless: true,
});

const page = await (await browser.newContext()).newPage();

const fontCheck = await page.evaluate(() => {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  ctx.font = '72px monospace';
  const fallbackWidth = ctx.measureText('mmmmm').width;

  ctx.font = '72px Arial, monospace';
  const arialWidth = ctx.measureText('mmmmm').width;

  return {
    arialAvailable: arialWidth !== fallbackWidth,
    arialWidth,
    fallbackWidth,
  };
});

console.log('字体检查:', fontCheck);

关键检查项:

  1. 字体可用性与目标平台匹配
  2. 文本度量不暴露实际宿主操作系统
  3. 无 "font access blocked" 警告

快速开始

  1. GitHub 下载 BotBrowser
  2. 使用 --bot-profile 加载与目标平台匹配的配置文件
  3. 使用指纹测试工具测试字体一致性
  4. 阅读 字体指南 了解详情
#fonts#fingerprinting#text-metrics#privacy