CPU 核心指纹:保护 hardwareConcurrency
navigator.hardwareConcurrency 如何暴露你的 CPU 核心数用于指纹追踪,以及在所有上下文中控制核心数报告的技术方案。
简介
navigator.hardwareConcurrency 属性返回浏览器可用的逻辑 CPU 核心数。它的设计初衷是帮助 Web 应用优化并行工作负载,让 JavaScript 代码决定生成多少个 Web Worker 或如何划分数据处理任务。但这同一个属性也暴露了有助于浏览器指纹追踪的硬件信息。一个拥有 6 个逻辑核心的设备远比拥有 8 个核心的设备少见,而拥有 64 个核心的服务器则一目了然。由于 CPU 核心数稳定、易于查询,且因硬件配置而异,它已成为指纹追踪系统的标准组成部分。本文解释 CPU 核心数如何参与指纹追踪、为什么简单的覆盖会导致问题,以及 BotBrowser 如何通过其配置文件系统提供一致的核心数报告。
隐私影响
CPU 核心数看似是一个低价值的指纹信号,但在综合考量下其贡献是显著的。任何指纹属性的价值取决于其在人群中的分布。根据 AmIUnique 项目的数据,navigator.hardwareConcurrency 约有 4.5 比特的熵值,意味着它可以区分大约 23 个不同的群组。虽然 23 个群组听起来不多,但每一比特的熵值在与其他信号组合时大致会使识别能力翻倍。
分布是高度偏斜的。值为 4 和 8 的占据了桌面浏览器的绝大多数。值为 2(较旧的机器、低端 Chromebook)、6(某些 Intel 配置)、12(Ryzen 5 处理器)、16(高端桌面)以及 16 以上的值则越来越少见。报告 24、32 或 64 核心的机器几乎可以确定是服务器或工作站,在正常浏览人群中极为罕见。
移动设备增加了更多差异化。大多数手机报告 4 或 8 个核心,但特定的 SoC 系列(Snapdragon、Exynos、MediaTek、Apple Silicon)将特定的核心数与其他可识别的属性配对。一部 hardwareConcurrency 为 8 且具有特定用户代理模式的手机可以将设备范围缩小到少数几个型号。
隐私问题被放大了,因为 hardwareConcurrency 在所有执行上下文中都可用:主线程、专用 Worker、共享 Worker 和 Service Worker。它不需要任何权限,且无法在不破坏使用它进行性能优化的合法 Web 应用的情况下被阻止。
技术背景
hardwareConcurrency 的工作原理
navigator.hardwareConcurrency 属性返回一个无符号长整数,表示可用的逻辑处理器数量。"逻辑处理器"是指物理核心乘以每核线程数(例如,一个 4 核 CPU 开启超线程后报告为 8)。
console.log(navigator.hardwareConcurrency); // 例如 8
该值在 Worker 上下文中也可用:
// 在 Web Worker 内部
console.log(self.navigator.hardwareConcurrency); // 相同值
为什么值会变化
有几个因素决定了 hardwareConcurrency 返回什么:
- 物理硬件。 CPU 核心数和线程数由处理器型号决定。Intel i5 处理器通常有 4-6 个核心带超线程。AMD Ryzen 7 处理器有 8 个核心带 SMT。Apple M 系列芯片在效率核心和性能核心之间有不同的核心数。
- 虚拟化。 虚拟机报告分配的 vCPU 数量,可能与宿主机不同。一个分配了 2 个 vCPU 的虚拟机报告
hardwareConcurrency为 2,而不管宿主机的实际核心数。 - 操作系统调度。 一些操作系统允许限制可见的处理器数量。容器环境(Docker、LXC)可以限制 CPU 可见性。
- 浏览器实现。 大多数浏览器报告来自操作系统的原始值。一些浏览器尝试过对值进行上限或分桶处理,但 Chromium 目前报告实际数量。
与其他信号的关联
CPU 核心数不是孤立存在的。追踪系统会将其与以下内容交叉引用:
navigator.deviceMemory- 一台 2 核 8 GB 内存的机器是合理的;一台 2 核 0.25 GB 内存的机器则暗示不同的设备类别。navigator.platform- "Linux x86_64" 平台配 4 核可以缩小到特定硬件。"Linux armv81" 配 8 核则暗示特定的 ARM 服务器或手机。- 用户代理。 浏览器版本和操作系统版本限制了哪些核心数是合理的。
- 性能计时。 实际的并行执行性能(通过计时侧信道测量)可以与报告的核心数进行比较。
报告的核心数与实际并行性能之间的不一致是一个强烈的信号,表明该值已被篡改。
常见保护方案及其局限性
手动覆盖 hardwareConcurrency,通过浏览器扩展或 JavaScript 注入,是最常见的方案。这改变了返回值,但会产生几个问题:
- 性能不一致。 如果你报告 4 核但实际机器有 16 核,并行工作负载(SharedArrayBuffer、Web Worker)的执行速度会比 4 核机器快。这种时序差异是可测量的。
- 原型检测。 对
navigator.hardwareConcurrency的 JavaScript 覆盖可以通过检查属性描述符、检查原型链或测量 getter 的执行时间来检测。 - Worker 上下文缺口。 一些扩展仅修改主线程的
navigator对象,而不修补 Worker 上下文。在 Web Worker 中运行的追踪脚本会看到真实值。
随机化值会创建不合理的配置。值为 7 或 13 不对应任何真实的 CPU 架构。值必须是 2 的幂或特定的已知配置才具有可信度。
使用固定的常见值(如始终报告 8)比随机化好,但如果与其他信号矛盾仍有风险。在用户代理声称是搭载 4 核 SoC 的 Android 手机时报告 8 核是不一致的。
根本要求是,报告的核心数必须与所有其他设备属性保持内部一致,并且不得与可观察的性能特征相矛盾。
BotBrowser 的引擎级方案
BotBrowser 通过其指纹配置文件系统在 Chromium 引擎级别控制 navigator.hardwareConcurrency。与 JavaScript 级别的修改相比,这种方案有多项优势。
配置文件驱动的值
每个 BotBrowser 指纹配置文件指定一个与配置设备实际 CPU 配置匹配的 hardwareConcurrency 值。Windows 10 桌面配置文件搭配 Intel i7 报告 8 或 16 核。Android 手机配置文件搭配 Snapdragon 888 报告 8 核。这些值来自真实设备配置,而非任意数字。
全上下文一致性
由于控制发生在引擎级别,每个上下文都报告相同的值:
- 主线程
navigator.hardwareConcurrency - 专用 Worker
self.navigator.hardwareConcurrency - 共享 Worker
self.navigator.hardwareConcurrency - Service Worker
self.navigator.hardwareConcurrency
不存在一个上下文返回真实值而另一个返回配置文件值的缺口。
跨信号一致性
配置文件中的 hardwareConcurrency 值是完整设备身份的一部分。它与以下内容保持一致:
- 用户代理字符串和平台
- deviceMemory 值
- 屏幕分辨率和其他硬件信号
- 浏览器品牌和版本
这确保了交叉引用检查不会发现不一致。
无 JavaScript 痕迹
由于值在引擎级别设置,不存在修改的属性描述符、非原生 getter 或与原生属性访问的时序差异。该属性的行为与在配置设备上完全一致。
配置和用法
基本配置文件加载
chrome --bot-profile="/path/to/profile.enc" \
--user-data-dir="$(mktemp -d)"
配置文件自动确定报告的核心数。
验证值
// 在浏览器控制台或自动化脚本中
console.log(navigator.hardwareConcurrency);
// 返回配置文件的核心数,而非你的实际硬件
Playwright 集成
const { chromium } = require('playwright');
const browser = await chromium.launch({
executablePath: '/path/to/botbrowser/chrome',
args: [
'--bot-profile=/path/to/profile.enc'
]
});
const page = await browser.newPage();
await page.goto('https://example.com');
const cores = await page.evaluate(() => navigator.hardwareConcurrency);
console.log(`Reported cores: ${cores}`);
// 验证 Worker 上下文匹配
const workerCores = await page.evaluate(() => {
return new Promise(resolve => {
const blob = new Blob([
'postMessage(self.navigator.hardwareConcurrency)'
], { type: 'application/javascript' });
const worker = new Worker(URL.createObjectURL(blob));
worker.onmessage = e => resolve(e.data);
});
});
console.log(`Worker cores: ${workerCores}`);
// 两个值应该相同
Puppeteer 集成
const puppeteer = require('puppeteer');
const browser = await puppeteer.launch({
executablePath: '/path/to/botbrowser/chrome',
defaultViewport: null,
args: [
'--bot-profile=/path/to/profile.enc'
]
});
const page = await browser.newPage();
await page.goto('https://example.com');
const cores = await page.evaluate(() => navigator.hardwareConcurrency);
console.log(`Reported cores: ${cores}`);
验证
值检查。 查询 navigator.hardwareConcurrency 并确认它与你加载的配置文件的预期值匹配。它不应反映你的实际硬件。
Worker 一致性。 创建一个 Web Worker 并在其中查询 self.navigator.hardwareConcurrency。该值应与主线程完全一致。
跨属性一致性。 验证核心数对于配置文件的平台、用户代理和设备内存是否合理。8 核配 8 GB 内存和 Windows 10 用户代理是合理的。64 核配 Android 用户代理则不合理。
跨会话稳定性。 使用相同配置文件跨多个会话查询该值。每次都应相同。
最佳实践
- 使用完整的配置文件。 不要尝试单独覆盖
hardwareConcurrency。该值必须与所有其他设备属性保持一致。BotBrowser 配置文件自动确保这一点。 - 选择具有真实配置的配置文件。 常见值(4、8)比不常见值(6、12、24)更能融入。选择适合你目标用例的配置文件。
- 在 Worker 上下文中测试。 始终验证基于 Worker 的查询返回与主线程相同的值。
- 避免手动覆盖核心数。 BotBrowser 配置文件设计有内部一致的配置。手动更改核心数而不调整其他属性会产生可检测的不一致。
常见问题
问:最常见的 hardwareConcurrency 值是什么? 答:在桌面端,4 和 8 是最常见的,合计占浏览器的 70% 以上。在移动端,4 和 8 同样占主导地位。值为 2、6、10、12、16 及更高的则越来越少见。
问:网站能否不管 hardwareConcurrency 报告什么都测量出我的实际核心数? 答:理论上,网站可以尝试使用 SharedArrayBuffer 和高精度计时器来测量并行执行性能。但实际上,浏览器安全缓解措施(降低的计时器精度、COOP/COEP 要求)使这变得困难。BotBrowser 的引擎级控制也有帮助,因为报告的值与配置文件的预期行为一致。
问:hardwareConcurrency 会随浏览器更新而改变吗? 答:不会。该值反映的是硬件和操作系统配置,而非浏览器版本。在同一台机器上,它在浏览器更新后保持稳定。
问:navigator.hardwareConcurrency 在所有浏览器中都可用吗? 答:是的。它在所有主流浏览器(Chrome、Firefox、Safari、Edge)中都受支持,并已作为 HTML 规范的一部分进行标准化。
问:为什么一些虚拟机报告与宿主机不同的核心数?
答:虚拟机报告的是虚拟机监控器分配的 vCPU 数量。一台 16 核的宿主机可能只给虚拟机分配 2 或 4 个 vCPU,这就是虚拟机内部 hardwareConcurrency 报告的值。
问:核心数是否影响 BotBrowser 中的 Web Worker 性能? 答:BotBrowser 控制的是报告值,而非实际的 CPU 调度。你的真实硬件核心仍然用于执行。配置文件的值只影响 JavaScript 查询该属性时看到的内容。
总结
navigator.hardwareConcurrency 是一个返回单个数字的简单属性,但它对浏览器指纹追踪贡献显著,尤其是与其他硬件信号结合时。BotBrowser 通过其配置文件系统在 Chromium 引擎级别控制该值,确保在所有 JavaScript 执行上下文中一致报告。由于该值是完整设备配置文件的一部分,它与用户代理、平台、内存以及所有其他身份信号保持一致。
相关主题请参阅什么是浏览器指纹、Navigator 属性保护、屏幕和窗口保护以及确定性浏览器行为。