文本测量指纹识别: 亚像素字体度量作为追踪信号
Canvas measureText() 返回的文本宽度值具有亚像素精度,由于字体渲染引擎的差异,这些值在不同操作系统之间各不相同。了解这些微小的数值差异如何成为可靠的平台指纹信号。
简介
当网站使用 Canvas API 的 measureText() 方法测量文本字符串的宽度时,结果是一个具有亚像素精度的浮点值。这些值在不同操作系统之间有所不同,因为每个平台使用具有自己内部算术模型的不同内置渲染引擎。这些差异非常微小,肉眼不可见,但它们是一致的、可重现的,并且可以通过 JavaScript 测量。
这种亚像素精度使 measureText() 成为追踪脚本可用的最可靠的平台识别信号之一。与需要渲染像素并哈希结果的 Canvas 图像指纹识别不同,文本测量指纹识别快速、轻量,并产生可以即时比较的直接数值。单次调用只需微秒级时间并返回一个可以识别你操作系统的值。当追踪脚本以多种字体大小测量多个字符串时,组合结果成为一个强大的指纹信号。
这使得文本测量成为社交媒体、银行、支付处理和电子商务网站上商业指纹系统积极使用的追踪向量。它不需要特殊权限,不产生用户可见的行为,并在浏览器会话之间产生稳定的结果。随着浏览器改进其他指纹向量的保护,文本测量正受到越来越多的关注,因为在 API 层面很难在不降低 Web 应用程序行为的情况下进行保护。
BotBrowser 的引擎级方法
BotBrowser 在浏览器引擎层面解决文本测量指纹识别,而不是通过 API 拦截或后处理。这是一个根本性的架构区别,可以在不被检测到且不破坏 Web 功能的情况下实现保护。
统一的字体渲染路径
BotBrowser 修改 Chromium 内部的字体渲染管道,确保字体度量计算在所有平台上使用一致的算术。无论 BotBrowser 运行在 macOS、Windows 还是 Linux 上,给定配置文件的字体缩放数学都会产生相同的结果。
这意味着当指纹配置文件指定特定设备配置时,measureText() 结果精确匹配该配置,直到最后一个有效数字。字体引擎不会简单地返回主机系统的原生值。相反,它使用配置文件定义的渲染模型来计算度量。
配置文件驱动的一致性
每个 BotBrowser 指纹配置文件编码了被分析设备的文本度量特征。当加载配置文件时,字体渲染引擎产生与原始设备匹配的测量值:
- 相同字体大小下的相同文本字符串返回相同的宽度值,无论主机操作系统如何。
- 在 macOS、Windows 和 Linux 上加载相同的配置文件产生位相同的
measureText()结果。 - 结果与其他与字体相关的信号(字体列表、Canvas 文本渲染、CSS 布局度量)内部一致。
这种一致性扩展到 TextMetrics 对象的所有属性,不仅仅是 width。actualBoundingBoxAscent、actualBoundingBoxDescent、fontBoundingBoxAscent、fontBoundingBoxDescent 和边界框左/右值都与配置文件匹配。
跨平台位相同的结果
关键的技术成就是位相同的结果。这意味着 measureText() 结果的浮点表示在各平台之间逐字节相同。没有"足够接近"的阈值。这些值在数学上是相同的。
这种级别的一致性只有通过引擎级控制才能实现。任何在渲染引擎之上操作的方法,如扩展程序、API 代理或 JavaScript 包装器,都无法实现位相同的结果,因为它无法控制字体引擎执行的实际算术。
BotBrowser 通过确保字体缩放数学使用相同的精度模型来实现这一点,无论主机平台如何。配置文件定义预期行为,引擎精确产生该行为。主机系统的原生字体渲染偏好不影响输出。
无可检测的工件
因为保护在渲染引擎内部运作:
measureText()返回具有完整双精度浮点精度的原生 JavaScriptNumber值,与未修改的浏览器完全相同。- 没有原型修改,没有函数包装,没有 JavaScript 层拦截。
- 这些值与 Canvas 像素渲染一致。在 Canvas 上绘制的文本然后逐像素测量与报告的
measureText()宽度匹配。 - 在不同字体大小和文本字符串的值分布中没有统计异常。
与其他信号的内部一致性
文本度量保护不是孤立运作的。配置文件确保 measureText() 值与所有其他可观察的字体和渲染信号一致。如果追踪脚本将文本度量与 Canvas 像素输出、字体枚举结果或 CSS 布局测量进行交叉验证,所有信号都与同一设备身份对齐。这种内部一致性至关重要,因为指纹系统越来越多地将不同信号类型之间的不匹配视为篡改指标。
配置和使用
CLI 用法
使用指纹配置文件启动 BotBrowser 以启用文本测量保护:
chrome --bot-profile="/path/to/profile.enc" \
--user-data-dir="$(mktemp -d)"
对于跨运行的确定性结果,添加噪声种子:
chrome --bot-profile="/path/to/profile.enc" \
--bot-noise-seed=12345 \
--user-data-dir="$(mktemp -d)"
结合时区、区域设置和代理设置以实现全面的身份一致性:
chrome --bot-profile="/path/to/profile.enc" \
--bot-noise-seed=12345 \
--proxy-server="socks5://user:pass@proxy:1080" \
--bot-config-timezone="America/New_York" \
--bot-config-locale="en-US" \
--bot-config-languages="en-US,en"
Playwright 集成
const { chromium } = require('playwright-core');
(async () => {
const browser = await chromium.launch({
executablePath: '/path/to/botbrowser/chrome',
args: [
'--bot-profile=/path/to/profile.enc',
'--bot-noise-seed=42',
],
headless: true,
});
const context = await browser.newContext({ viewport: null });
const page = await context.newPage();
await page.goto('https://example.com');
// measureText() results will be consistent with the loaded profile
const metrics = await page.evaluate(() => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
ctx.font = '16px Arial';
const measurement = ctx.measureText('Sample text for measurement');
return {
width: measurement.width,
actualBoundingBoxAscent: measurement.actualBoundingBoxAscent,
actualBoundingBoxDescent: measurement.actualBoundingBoxDescent,
};
});
console.log('Text metrics:', metrics);
await browser.close();
})();
Puppeteer 集成
const puppeteer = require('puppeteer-core');
(async () => {
const browser = await puppeteer.launch({
executablePath: '/path/to/botbrowser/chrome',
args: [
'--bot-profile=/path/to/profile.enc',
'--bot-noise-seed=42',
],
headless: true,
defaultViewport: null,
});
const page = await browser.newPage();
await page.goto('https://example.com');
// Verify text measurement from the loaded profile
const metrics = await page.evaluate(() => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
ctx.font = '16px Arial';
const m = ctx.measureText('Sample text for measurement');
return { width: m.width };
});
console.log('Text width:', metrics.width);
await browser.close();
})();
验证
使用配置文件启动 BotBrowser 后,验证文本测量保护是否激活:
const width = await page.evaluate(() => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
ctx.font = '16px Arial';
return ctx.measureText('Verification string').width;
});
console.log('Text width:', width);
检查要点:
- 宽度值是全精度浮点数,与具有配置文件目标平台的真实设备返回的值一致。
- 重新加载页面并重复测量产生相同的值。
- 使用相同的配置文件和噪声种子重启浏览器产生相同的值。
- 访问 BrowserLeaks 或 CreepJS 等指纹测试站点,确认没有跨信号不一致被标记。
最佳实践
始终使用完整的配置文件
文本测量保护作为 BotBrowser 完整指纹配置文件的一部分工作。配置文件确保文本度量与所有其他浏览器信号一致: Canvas 渲染、字体列表、WebGL 参数和系统信息。加载配置文件是文本测量保护激活的必要条件。
结合网络身份
文本度量标识平台。网络信号标识位置。为了一致的身份,将指纹配置文件与适当的代理和区域设置结合:
chrome --bot-profile="/path/to/profile.enc" \
--proxy-server="socks5://user:pass@proxy:1080" \
--bot-config-timezone="Europe/London" \
--bot-config-locale="en-GB" \
--bot-config-languages="en-GB,en"
使用确定性模式进行测试
运行自动化测试或研究实验时,使用 --bot-noise-seed 确保可重现的结果。这允许你验证文本度量在测试运行之间保持稳定,并检测度量一致性中的任何回退。
使用多种字体配置进行测试
验证保护时,使用各种字体族和大小进行测试。在测试集中包括常见的 Web 字体和系统默认值。这确保了配置文件的文本度量一致性在追踪脚本通常探测的字体范围内得到全面覆盖。
保持配置文件和 BotBrowser 更新
文本测量指纹识别持续演变。最近的浏览器版本添加了新的 TextMetrics 属性,追踪脚本更新了其收集以包括这些新信号。保持 BotBrowser 安装和配置文件更新,以确保覆盖新的可利用属性。
常见问题
什么是文本测量指纹识别?
文本测量指纹识别使用 Canvas measureText() API 来确定浏览器运行在哪个平台上。该 API 返回具有亚像素精度的文本宽度值,这些值在操作系统之间有所不同,因为每个操作系统使用具有自己内部算术模型的不同内置渲染引擎。通过以特定大小测量特定文本字符串,追踪脚本可以识别操作系统并区分各个设备。
这与 Canvas 指纹识别有什么不同?
Canvas 指纹识别将可视内容(文本、形状、渐变)渲染到 Canvas 元素并对生成的像素数据进行哈希。文本测量指纹识别使用 measureText() 获取数值宽度值而不渲染任何可见内容。文本测量更快,产生直接的数值结果,并提供不同类型的信号。两种技术都依赖于平台特定的渲染差异,但它们操作 Canvas API 的不同输出。你可以在我们的 Canvas 指纹识别 指南中了解更多关于 Canvas 指纹识别的信息。
BotBrowser 在所有平台上产生相同的值吗?
是的。当加载相同的指纹配置文件时,BotBrowser 在 macOS、Windows 和 Linux 上产生位相同的 measureText() 结果。这是通过引擎级控制字体渲染数学实现的,确保相同的配置文件始终产生相同的数值结果,无论主机操作系统如何。
文本测量保护会破坏网站吗?
不会。BotBrowser 不会阻止或修改 measureText() API。该 API 正常工作,返回 Web 应用程序可用于布局、文本截断、图表渲染和所有其他合法用途的准确文本宽度值。这些值只是与加载的配置文件一致,而不是与主机系统的原生渲染一致。
这与字体指纹识别有什么关系?
文本测量指纹识别与字体指纹识别密切相关,但针对不同的信号。字体指纹识别通过检查特定字体族是否可用来识别安装了哪些字体。文本测量指纹识别通过检查亚像素度量值来识别渲染引擎如何处理这些字体。两个信号都源自字体渲染堆栈,两者都由 BotBrowser 的引擎级保护覆盖。有关更广泛的概述,请参阅我们关于浏览器指纹识别的文章。
我可以在现有的自动化框架中使用 BotBrowser 的文本测量保护吗?
是的。BotBrowser 与 Playwright、Puppeteer、Selenium 和其他自动化框架集成。加载指纹配置文件时,文本测量保护会自动激活。除了传递 --bot-profile 标志外,不需要额外的配置或代码更改。有关设置说明,请参阅 Playwright 入门指南 和 Puppeteer 入门指南。
这种保护覆盖所有 TextMetrics 属性吗?
是的。BotBrowser 的保护覆盖完整的 TextMetrics 属性集,包括 width、actualBoundingBoxLeft、actualBoundingBoxRight、actualBoundingBoxAscent、actualBoundingBoxDescent、fontBoundingBoxAscent 和 fontBoundingBoxDescent。所有值在所有平台上都与加载的配置文件一致。
总结
文本测量指纹识别利用 Canvas measureText() API 的亚像素精度,通过渲染引擎识别浏览器。因为每个操作系统使用具有自己内部算术模型的不同内置渲染引擎,相同字体大小下的相同文本在每个平台上产生可测量的不同宽度值。这些差异是一致的、可重现的,并被商业追踪系统积极收集。
BotBrowser 通过在 Chromium 内部控制字体渲染数学,在浏览器引擎层面解决文本测量指纹识别。当加载指纹配置文件时,字体缩放计算在 macOS、Windows 和 Linux 上产生位相同的结果。没有 API 级别的钩子,没有原型修改,也没有统计异常。这些值与配置文件中所有其他字体和 Canvas 信号内部一致,确保指纹脚本的交叉验证检查不会发现不匹配。
结合 BotBrowser 对 Canvas 渲染、字体枚举 和跨平台配置文件一致性的保护,文本测量保护关闭了当今追踪系统可用的最精确的指纹向量之一。 API。该 API 正常工作,返回准确的文本宽度值,Web 应用程序可以将其用于布局、文本截断、图表渲染和所有其他合法用途。这些值只是与加载的配置文件一致,而不是与宿主系统的原生渲染一致。
这与字体指纹识别有什么关系?
文本测量指纹识别与字体指纹识别密切相关,但针对不同的信号。字体指纹识别通过检查特定字体系列是否可用来识别安装了哪些字体。文本测量指纹识别则通过检查亚像素度量值来识别渲染引擎如何处理这些字体。两种信号都源自字体渲染栈,都受到 BotBrowser 引擎级保护的覆盖。有关更广泛的概述,请参阅我们关于浏览器指纹识别的文章。
我可以将 BotBrowser 的文本测量保护与现有的自动化框架一起使用吗?
可以。BotBrowser 与 Playwright、Puppeteer、Selenium 和其他自动化框架集成。当加载指纹配置文件时,文本测量保护会自动激活。除了传递 --bot-profile 标志外,不需要额外的配置或代码更改。请参阅 Playwright 入门指南和 Puppeteer 入门指南了解设置说明。
此保护是否覆盖所有 TextMetrics 属性?
是的。BotBrowser 的保护覆盖了完整的 TextMetrics 属性集,包括 width、actualBoundingBoxLeft、actualBoundingBoxRight、actualBoundingBoxAscent、actualBoundingBoxDescent、fontBoundingBoxAscent 和 fontBoundingBoxDescent。所有值在所有平台上都与加载的配置文件保持一致。
总结
文本测量指纹识别利用 Canvas measureText() API 的亚像素精度,通过渲染引擎来识别浏览器。由于每个操作系统使用具有自身内部算术模型的不同内置渲染引擎,相同字体大小下的相同文本在每个平台上会产生可测量的不同宽度值。这些差异是一致的、可重复的,并且被商业追踪系统积极收集。
BotBrowser 通过控制 Chromium 内部的字体渲染数学运算,在浏览器引擎层面解决文本测量指纹识别问题。当加载指纹配置文件时,字体缩放计算在 macOS、Windows 和 Linux 上产生位级相同的结果。没有 API 级别的钩子,没有原型修改,没有统计异常。这些值与配置文件中所有其他字体和 Canvas 信号在内部保持一致,确保指纹识别脚本的交叉验证检查不会发现不匹配。
结合 BotBrowser 对 Canvas 渲染、字体枚举和跨平台配置文件一致性的保护,文本测量保护封堵了当今追踪系统可用的最精确的指纹识别向量之一。