性能时间指纹:硬件信号追踪
Performance.now() 精度、硬件并发数和设备内存如何成为追踪信号,以及如何在引擎级别控制它们。
简介
Performance API 是最基础的浏览器接口之一,最初设计用于帮助开发人员测量页面加载时间、诊断瓶颈和优化用户体验。performance.now()、performance.mark() 和 performance.measure() 等函数为代码执行基准测试提供高分辨率时间戳。除了这些计时函数外,navigator.hardwareConcurrency 和 navigator.deviceMemory 等属性报告设备的 CPU 核心数和可用 RAM。
虽然这些 API 服务于合法目的,但它们也暴露了在设备之间变化的硬件特定特征。一台有 8 个 CPU 核心和 16 GB RAM 的笔记本电脑产生的计时结果和硬件报告,与一台有 16 个核心和 32 GB 的台式机可测量地不同。这些差异在浏览器会话间保持稳定,经得住 Cookie 清除,且在隐身模式中持续存在,使它们成为追踪单个设备的可靠信号。
隐私影响
性能时间信号代表了一种特别隐蔽的指纹识别形式,因为它们与用户无法轻易改变的物理硬件特征深度绑定。与用户可以清除的 Cookie 或本地存储不同,计时信号源自设备执行 JavaScript、渲染图形和处理数据的基本速度。
包括普林斯顿大学和鲁汶天主教大学在内的机构的研究表明,基于计时的指纹识别可以高精度地区分设备。2019 年的一项研究表明,将 performance.now() 测量与硬件并发值结合,即使其他标识符已被清除,也能正确识别超过 80% 的回访者。
问题超出了单个信号的范围。当追踪器同时收集 navigator.hardwareConcurrency(CPU 核心)、navigator.deviceMemory(RAM)和微基准测试执行时间时,组合指纹变得高度独特。一个有 6 核心、8 GB 内存和特定 JavaScript 执行速度特征的设备显著缩小了识别窗口。这些信号不需要用户权限,不生成提示,对被追踪的人来说是不可见的。
技术背景
要理解性能时间为什么在设备之间变化,需要了解什么影响 JavaScript 执行速度以及暴露硬件信息的 API。
Performance.now() 和高分辨率时间
performance.now() 返回以毫秒为单位的高分辨率时间戳,具有微秒精度。实际分辨率取决于浏览器的安全缓解措施(许多浏览器降低精度以防止 Spectre 类攻击),但即使是降低的精度也会揭示与 CPU 时钟速度、缓存层次结构和指令流水线相关的计时特征。
当网站运行微基准测试时,例如迭代一个循环 100,000 次或计算 SHA-256 哈希,执行时间直接受设备硬件的影响。快速桌面 CPU 比移动处理器更快完成相同工作,这种差异是可测量且可重复的。
硬件并发数
navigator.hardwareConcurrency 返回浏览器可用的逻辑处理器核心数。此值反映物理 CPU 配置,在会话间不会改变。常见值从 2(低端移动设备)到 32 或更多(高端工作站)。分布不均匀:某些核心数(4、8、12、16)比其他值常见得多,这意味着不常见的值成为强识别符。
设备内存
navigator.deviceMemory 返回设备 RAM 的近似值(GB),四舍五入到最接近的 2 的幂(0.25、0.5、1、2、4、8)。虽然四舍五入降低了精度,但此值仍然为整体指纹做出贡献。报告 8 GB 内存配 8 核心的设备与报告 4 GB 配 4 核心的设备指纹不同。
为什么这些值组合在一起很重要
单独来看,每个信号的熵有限。但组合起来,它们形成一个硬件特征:特定的执行速度、特定的核心数和特定的内存等级。与 Canvas、WebGL 和字体等其他指纹信号叠加时,计时特征对设备识别有重要贡献。
常见保护方法及其局限性
VPN 和代理服务器
VPN 改变你的 IP 地址,但对性能时间完全没有影响。所有计时测量和硬件属性查询都在浏览器本地进行。使用同一 VPN 的两台设备仍然报告完全不同的硬件特征和执行速度。
隐身和隐私浏览
隐私浏览模式清除 Cookie 和历史记录,但不修改 navigator.hardwareConcurrency、navigator.deviceMemory 或底层硬件的执行速度。你在隐身模式下的性能指纹与在正常窗口中的指纹完全相同。
浏览器扩展
试图修改硬件属性的扩展面临根本性的局限:
- 如果
navigator.hardwareConcurrency报告 4 核心但 Web Worker 微基准测试显示 8 个并行线程同时执行,这些值会冲突。 - 如果
navigator.deviceMemory报告 2 GB 但performance.now()对内存密集型操作的计时与 16 GB 一致(无垃圾回收暂停),这种不匹配是明显的。 - 扩展注入的覆盖可以通过检查属性描述符、原型链或在扩展执行前在新上下文中运行代码来检测。
随机化
一些隐私工具通过向 performance.now() 添加噪声来随机化计时值。虽然这防止了稳定的识别,但随机偏移的时间戳会破坏合法的性能监控,并创建自己的可检测模式。具有人工抖动的时间戳序列看起来与自然硬件变化不同。
BotBrowser 的引擎级方法
BotBrowser 对性能时间保护采取了根本不同的方法。BotBrowser 不是在事后拦截 API 调用或添加噪声,而是在浏览器引擎级别控制计时信号,以产生与完整设备配置文件匹配的内部一致的结果。
基于配置文件的硬件身份
当你加载指纹配置文件时,BotBrowser 配置所有与硬件相关的值以匹配该配置文件的目标设备:
chrome --bot-profile="/path/to/profile.enc" \
--user-data-dir="$(mktemp -d)"
这将 navigator.hardwareConcurrency 和 navigator.deviceMemory 设置为配置文件的配置值。这些不是 JavaScript 覆盖;它们在引擎级别在任何页面代码执行之前应用。属性描述符检查、原型检查和新上下文评估都返回相同的值,因为引擎本身报告它们。
性能时间缩放
BotBrowser 提供 --bot-time-scale 标志来控制 JavaScript 操作的表观执行速度:
chrome --bot-profile="/path/to/profile.enc" \
--bot-time-scale=0.92
该标志接受小于 1.0 的浮点数(典型范围:0.80-0.99)。缩放值 0.92 将 performance.now() 间隔压缩 8%,减少可能识别设备的时序偏差信号。缩放均匀应用于所有计时源,因此微基准测试、performance.mark() 和 performance.measure() 都产生与相同硬件等级匹配的内部一致结果。
性能时间种子
2026 年 3 月新增的 --bot-time-seed(ENT Tier2)提供更精细的性能计时保护。--bot-time-scale 压缩计时间隔以减少偏差信号,而 --bot-time-seed 控制各个浏览器操作的计时分布。
chrome --bot-profile="/path/to/profile.enc" \
--bot-time-scale=1.0 \
--bot-time-seed=12345
--bot-time-seed=<integer> 接受 1 到 UINT32_MAX 的整数种子(0 禁用该功能)。每个种子产生唯一、稳定的性能配置文件,覆盖多种浏览器操作的计时多样化,确保每个实例都拥有独特且一致的计时签名。
种子还覆盖 performance.getEntries()、performance.getEntriesByType("navigation") 和 performance.timing 值的 per-session 重分布,使导航计时模式对每个种子都是唯一的。
这是完全确定性的:相同的种子始终产生相同的计时指纹。与 --bot-time-scale 结合使用,可以同时获得宏观级别的速度控制和微观级别的计时多样性。
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-time-scale=1.0',
'--bot-time-seed=12345',
],
headless: true,
});
const context = await browser.newContext({ viewport: null });
const page = await context.newPage();
await page.goto('https://example.com');
// 计时分布对种子 12345 是唯一的
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-time-scale=1.0',
'--bot-time-seed=12345',
],
headless: true,
defaultViewport: null,
});
const page = await browser.newPage();
await page.goto('https://example.com');
await browser.close();
})();
跨信号一致性
引擎级控制的关键优势是一致性。当 BotBrowser 通过配置文件报告 4 个 CPU 核心和 4 GB 内存时,JavaScript 执行的计时特征也与该硬件等级一致。Web Workers 以与报告核心数一致的并行性执行。内存分配模式与报告的内存等级匹配。浏览器报告的和实际表现之间没有矛盾。
确定性模式
对于测试、研究和 CI/CD 流水线,BotBrowser 通过噪声种子标志支持完全确定性的计时:
chrome --bot-profile="/path/to/profile.enc" \
--bot-time-scale=1.0 \
--bot-noise-seed=42 \
--user-data-dir="$(mktemp -d)"
相同的配置文件、时间缩放和噪声种子在不同会话、主机和操作系统之间产生相同的计时指纹。
配置和使用
基本 CLI 用法
chrome --bot-profile="/path/to/profile.enc" \
--bot-time-scale=1.0 \
--user-data-dir="$(mktemp -d)"
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-time-scale=1.0',
],
headless: true,
});
const context = await browser.newContext({ viewport: null });
const page = await context.newPage();
await page.goto('https://example.com');
// 硬件值和计时信号匹配加载的配置文件
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-time-scale=0.92',
'--bot-noise-seed=42',
],
headless: true,
defaultViewport: null,
});
const page = await browser.newPage();
await page.goto('https://example.com');
await browser.close();
})();
组合配置
为获得全面保护,将计时控制与其他配置文件设置结合:
chrome --bot-profile="/path/to/profile.enc" \
--bot-time-scale=1.0 \
--bot-noise-seed=42 \
--proxy-server="socks5://user:pass@proxy:1080" \
--bot-config-timezone="America/New_York" \
--bot-config-locale="en-US"
验证
使用配置文件启动 BotBrowser 后,验证硬件值和计时是否受控:
// 检查硬件属性
console.log('CPU Cores:', navigator.hardwareConcurrency);
console.log('Device Memory:', navigator.deviceMemory, 'GB');
// 测量计时一致性
async function measureTiming() {
const results = [];
for (let i = 0; i < 5; i++) {
const start = performance.now();
for (let j = 0; j < 100000; j++) Math.sqrt(j);
results.push(performance.now() - start);
}
return results;
}
const timings = await measureTiming();
console.log('Timing samples:', timings);
console.log('Variance:', Math.max(...timings) - Math.min(...timings));
需要检查的内容:
navigator.hardwareConcurrency匹配配置文件的核心数navigator.deviceMemory匹配配置文件的内存配置- 使用相同配置文件和种子时,计时样本在运行间行为一致
- 公共指纹测试工具(CreepJS、BrowserLeaks)无异常
最佳实践
-
始终使用完整配置文件。 硬件值、计时和其他信号必须一致。配置文件确保每个指纹面的一致性。
-
有意使用
--bot-time-scale。 设置缩放值以减少配置文件目标硬件等级的时序偏差。典型值范围为 0.80 到 0.99,值越低压缩计时间隔越大。 -
CI/CD 使用确定性模式。 将
--bot-noise-seed与--bot-time-scale结合,在自动化测试流水线中获得可重复的结果。 -
将代理地理位置与配置文件身份匹配。 为美国设备配置的配置文件应使用美国代理。计时一致性与不匹配的地理位置结合会削弱整体指纹一致性。
-
避免混合使用修改计时的浏览器扩展。 BotBrowser 在引擎级别原生处理所有计时保护。修改
performance.now()或硬件属性的第三方扩展会产生冲突。
常见问题
performance.now() 精度在不同浏览器间有区别吗?
是的。不同浏览器实现了不同级别的时间戳精度,部分作为针对 Spectre 类侧信道攻击的缓解措施。Chrome、Firefox 和 Safari 各自以不同方式降低精度。BotBrowser 的配置文件系统考虑了目标浏览器预期的精度特征。
网站能检测到计时值被控制了吗?
如果计时控制应用不一致(例如修改了 performance.now() 但没有修改 Date.now() 或 performance.mark()),网站可以检测到差异。BotBrowser 在引擎级别对所有计时源均匀应用计时缩放,防止跨 API 不一致。
--bot-time-scale 会影响实际页面性能吗?
不会。该标志控制计时值的报告方式,而不是 JavaScript 实际执行的速度。页面以全速加载和运行;只有报告给 JavaScript 的测量值反映缩放的计时。
硬件并发数如何与 Web Workers 交互?
BotBrowser 确保报告的 navigator.hardwareConcurrency 值与可以并行运行的 Web Workers 数量一致。配置文件同时管理这两个值。
我可以为不同配置文件使用不同的时间缩放吗?
可以。每个浏览器实例使用自己的 --bot-time-scale 值。你可以同时运行多个实例,使用不同的配置文件和不同的计时特征。
SharedArrayBuffer 计时呢?
SharedArrayBuffer 与 Atomics 结合可以用作高分辨率计时器。BotBrowser 的引擎级计时控制也适用于共享内存计时通道,保持所有计时测量方法的一致性。
计时指纹在浏览器更新后还存在吗?
在未受保护的浏览器上,是的。计时指纹与硬件绑定,而非浏览器版本。BotBrowser 配置文件有版本控制,因此你可以在配置文件更新中保持一致的计时特征。
--bot-time-seed 和 --bot-time-scale 有什么区别?
它们服务于互补的目的。--bot-time-scale 压缩 performance.now() 间隔(接受小于 1.0 的浮点数,典型范围 0.80-0.99)以减少时序偏差信号。--bot-time-seed 控制各个浏览器操作的计时分布,为每个种子值产生唯一的逐操作计时配置文件。你可以同时使用两者:--bot-time-scale 设置宏观级别的计时压缩,而 --bot-time-seed 在该配置文件内添加微观级别的计时多样性。
总结
Performance API 暴露了作为稳定追踪信号的硬件特征。performance.now()、navigator.hardwareConcurrency 和 navigator.deviceMemory 一起形成了在会话间持续存在并经得住常见隐私措施的硬件身份。BotBrowser 通过其配置文件系统在浏览器引擎级别控制所有这些信号,使用 --bot-time-scale 进行计时间隔压缩,使用 --bot-time-seed 实现逐操作计时多样化。这些标志共同确保与目标设备身份匹配的内部一致结果。相关保护请参阅 Canvas 指纹保护、WebGL 指纹控制和帧率控制。