返回博客
指纹

Client Hints 指纹识别: HTTP 头部如何暴露浏览器身份

sec-ch-ua 等 Client Hints 头部在每次 HTTP 请求中暴露浏览器品牌、版本、平台和设备信息。了解这些头部中的不一致性如何产生可追踪信号,以及如何保持一致性。

简介

浏览器发送的每个 HTTP 请求都以 Client Hints 头部的形式携带身份信息。在任何 JavaScript 执行之前,在任何页面内容加载之前,Sec-CH-UASec-CH-UA-PlatformSec-CH-UA-Mobile 等头部已经以结构化、机器可读的格式向服务器传输了浏览器品牌、版本和操作系统详情。当服务器请求或 JavaScript 调用 navigator.userAgentData.getHighEntropyValues() 时,还可获得包括 CPU 架构、操作系统版本、设备型号和完整版本字符串在内的高熵值。

这些头部被设计为对单一 User-Agent 字符串的隐私改进,通过选择加入机制提供结构化数据。实际上,每个请求默认发送的头部已经为浏览器指纹识别贡献了有意义的熵。更重要的是,Client Hints 值在会话内所有请求类型之间的内部一致性,以及 HTTP 头部与 JavaScript API 之间的对齐,已经成为一个隐私关键表面。网络层报告的内容与 JavaScript 层暴露的内容之间的任何不一致都会产生可追踪信号,服务器端可以轻易观察到。

BotBrowser 在引擎层面解决这个问题,确保每个 Client Hints 值都由配置文件驱动,在所有请求类型和执行上下文中保持一致,并在 HTTP 头部和 JavaScript API 之间对齐。

BotBrowser 的解决方案

配置文件驱动的 Client Hints

BotBrowser 从真实浏览器实例捕获的配置文件生成所有 Client Hints 值。加载配置文件时,引擎从配置文件数据构建完整的 Client Hints 配置,包括:

  • 完整的 Sec-CH-UA 品牌列表,包含与配置文件 Chrome 版本对应的正确 GREASE 品牌
  • 匹配真实 Chrome 行为的确定性品牌排序
  • 平台、平台版本、架构、位数和型号值
  • 包含所有品牌条目及其完整版本字符串的完整版本列表

此配置在浏览器启动时设置一次,并统一应用于整个会话中的每个请求。没有辅助代码路径,没有按请求重新生成,也没有依赖上下文的变化。

跨请求一致性

所有请求类型使用相同的品牌列表。导航请求、子资源请求(脚本、样式表、图片、fetch 调用)、worker 请求、预取请求和 service worker 请求都接收相同的 Sec-CH-UA 头部,因为它们都从相同的配置文件派生配置。单次页面加载可能生成数十个 HTTP 请求,每一个都携带相同的 Client Hints 值。

JavaScript 和 HTTP 对齐

BotBrowser 确保 JavaScript 中的 navigator.userAgentData.brands 返回与 Sec-CH-UA HTTP 头部中出现的相同品牌。同样,navigator.userAgentData.getHighEntropyValues() 返回与高熵 Client Hints 头部一致的值。这种对齐扩展到浏览器中的每个执行上下文:

  • 主线程(window 上下文)
  • 专用 web worker
  • 共享 worker
  • Service worker
  • Worklet

每个上下文报告相同的 Client Hints 值,因为它们都源自相同的配置文件。

跨会话稳定性

在多个浏览器会话中使用相同的配置文件时,BotBrowser 每次都产生相同的 Client Hints 配置。给定相同的配置文件,GREASE 品牌、版本、排序和所有元数据值都是确定性的。这对于需要在重启之间维持持久浏览器身份的场景很重要。

品牌覆盖

当需要呈现不同的浏览器品牌(例如 Edge 而不是 Chrome)时,BotBrowser 会自动调整所有 Client Hints 表面:

chrome --bot-profile="/path/to/profile.enc" \
       --bot-config-browser-brand=edge \
       --bot-config-brand-full-version=136.0.3240.76

这会在一个连贯的操作中更新 Sec-CH-UA 品牌、Sec-CH-UA-Full-Version-Listnavigator.userAgentData.brandsUser-Agent 字符串。所有表面都反映相同的品牌身份。

配置和使用

基本配置文件用法

对于大多数用例,加载配置文件就足够了:

chrome --bot-profile="/path/to/profile.enc"

配置文件包含所有 Client Hints 配置。一致的 Client Hints 行为不需要额外的标志。

Playwright 集成

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

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

  const context = await browser.newContext();
  const page = await context.newPage();
  await page.goto('https://example.com');

  // Read low-entropy Client Hints from JavaScript
  const brands = await page.evaluate(() =>
    navigator.userAgentData.brands.map(b => `${b.brand};v="${b.version}"`)
  );
  console.log('Brands:', brands);

  // Read high-entropy Client Hints
  const hints = await page.evaluate(async () => {
    const data = await navigator.userAgentData.getHighEntropyValues([
      'platformVersion', 'architecture', 'bitness',
      'fullVersionList', 'model'
    ]);
    return {
      platform: data.platform,
      platformVersion: data.platformVersion,
      architecture: data.architecture,
      bitness: data.bitness,
      fullVersionList: data.fullVersionList,
      model: data.model,
    };
  });
  console.log('High-entropy hints:', hints);

  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',
    ],
    headless: true,
    defaultViewport: null,
  });

  const page = await browser.newPage();
  await page.goto('https://example.com');

  // Verify Client Hints from the loaded profile
  const jsHints = await page.evaluate(() => ({
    brands: navigator.userAgentData.brands,
    mobile: navigator.userAgentData.mobile,
    platform: navigator.userAgentData.platform,
  }));
  console.log('Client Hints:', jsHints);

  await browser.close();
})();

验证

检查 JavaScript Client Hints

打开页面并验证 navigator.userAgentData 返回预期值:

// In browser console or via automation
const brands = navigator.userAgentData.brands;
console.log('Brands:', JSON.stringify(brands, null, 2));
console.log('Platform:', navigator.userAgentData.platform);
console.log('Mobile:', navigator.userAgentData.mobile);

const high = await navigator.userAgentData.getHighEntropyValues([
  'platformVersion', 'architecture', 'bitness',
  'fullVersionList', 'model', 'wow64'
]);
console.log('High-entropy:', JSON.stringify(high, null, 2));

使用在线验证工具

访问 BrowserLeaksCreepJSCover Your Tracks 查看您的 Client Hints 值以及其他指纹数据。确认报告的品牌、平台和版本与您期望的配置文件匹配。另请参阅 BotBrowser 的指纹验证指南 获取全面的验证指导。

最佳实践

始终使用配置文件

不要手动构建 Client Hints 值。Chrome 版本、GREASE 品牌和品牌排序之间的关系是版本相关的,并且随每个版本变化。配置文件从真实浏览器实例捕获这些关系并精确复制它们。

将 Client Hints 与 User-Agent 匹配

如果使用 --user-agent 覆盖 User-Agent 字符串,请确保 --bot-config-browser-brand--bot-config-ua-full-version 标志对齐。BotBrowser 会自动生成匹配的 Client Hints,但 User-Agent 字符串覆盖是单独应用的,必须保持一致。

保持配置文件更新

Client Hints 随每个 Chrome 版本变化。新的 GREASE 品牌被引入,版本号也在递增。使用带有旧版本号的过时配置文件本身就可能成为一个区分信号。定期从 BotBrowser 配置文件仓库 更新您的配置文件以匹配当前浏览器版本。

不要混合 Client Hints 来源

避免将 BotBrowser 基于配置文件的 Client Hints 与 CDP 覆盖或框架级 User-Agent 更改结合使用。每一种都在不同的层操作,组合使用会产生不一致性。让 BotBrowser 通过其配置文件系统处理所有身份信号。

监控服务器请求的 Hints

一些服务器通过 Accept-CH 请求额外的高熵 hints。BotBrowser 使用配置文件中的值响应这些请求。如果您正在针对特定服务进行测试,请检查它请求了哪些 hints,并验证 BotBrowser 的响应是否完整且一致。

常见问题

User-Agent 和 Client Hints 之间有什么区别?

User-Agent 头部是一个包含浏览器、版本和操作系统信息的自由格式单一字符串。Client Hints(Sec-CH-UA-* 头部)以结构化键值对的形式提供相同的信息。现代 Chromium 浏览器同时发送两者,服务器会比较它们的一致性。请参阅自定义 User Agent 获取详细比较。

我可以用 Playwright 或 Puppeteer 手动设置 Client Hints 吗?

Playwright 和 Puppeteer 允许覆盖 User-Agent 字符串,并通过 CDP 覆盖一些 Client Hints 元数据。然而,这些覆盖不涵盖所有表面(worker、service worker、高熵值),并且需要手动管理 GREASE 品牌。BotBrowser 基于配置文件的方法涵盖每个表面,避免了部分覆盖造成不一致的风险。

Client Hints 在无头模式下工作吗?

是的。BotBrowser 在无头和有头模式下发送完全相同的 Client Hints。无论显示模式如何,Sec-CH-UA 头部和 navigator.userAgentData 值都是相同的。

Client Hints 与 navigator 属性有什么关系?

navigator.userAgentData 是 Client Hints 的 JavaScript 接口。通过 navigator.userAgentData 暴露的品牌、平台和移动标志直接对应于 Sec-CH-UASec-CH-UA-PlatformSec-CH-UA-Mobile HTTP 头部。BotBrowser 确保它们始终对齐。有关 navigator 属性的更多信息,请参阅 Navigator 属性指纹识别

如果服务器不请求高熵 hints 会怎样?

BotBrowser 仅在服务器通过 Accept-CH 请求时才发送高熵 Client Hints 头部,遵循标准 Chromium 行为。然而,JavaScript getHighEntropyValues() API 始终可用。BotBrowser 确保 HTTP 头部(发送时)和 JavaScript API 都从配置文件返回一致的值。

Client Hints 头部可以在没有 JavaScript 的情况下用于追踪吗?

是的。默认的 Sec-CH-UASec-CH-UA-MobileSec-CH-UA-Platform 头部随每个 HTTP 请求发送,即使 JavaScript 被禁用。这就是为什么 BotBrowser 的保护在引擎层面运作,而不是依赖 JavaScript 层面的干预。

为保持 Client Hints 准确性,我应该多久更新一次配置文件?

每个 Chrome 版本都可能引入 Client Hints 值的变化。建议在每个 Chrome 主要版本(大约每四周)更新配置文件,以确保值与当前浏览器版本匹配。

BotBrowser 支持 Edge、Brave 和 Opera 的品牌覆盖吗?

是的。--bot-config-browser-brand 标志支持 chromeedgebraveoperawebview。当指定品牌时,BotBrowser 会更新所有 Client Hints 头部、JavaScript API 和 User-Agent 字符串,以一致地反映所选品牌。

总结

Client Hints 头部在每个 HTTP 请求中携带浏览器身份信息,创建了一个在任何页面内容加载之前就运作的隐私关键表面。这些值在请求类型、执行上下文以及 HTTP 头部和 JavaScript API 之间的一致性对于隐私保护至关重要。

BotBrowser 在引擎层面处理这一点。从真实浏览器实例捕获的配置文件定义了所有 Client Hints 值。这些值从配置文件生成一次,并统一应用于每种请求类型、每个执行上下文和每个 API 表面。结果是与真实浏览器输出匹配的 Client Hints 行为,因为它由相同的 Chromium 引擎使用配置文件驱动的配置产生。

有关相关主题的更多信息,请参阅:

#client hints#sec-ch-ua#fingerprinting#GREASE#user agent#privacy#browser identity#HTTP headers

准备好保护浏览器指纹了吗?

BotBrowser 提供引擎级指纹控制和真实设备配置文件。免费开始使用或探索所有功能。