指纹

噪声种子:确定性 RNG 控制指纹

深入探讨确定性噪声种子如何在会话和 CI/CD 管道中产生一致的 Canvas、WebGL 和 Audio 指纹。

文档中心

想直接看维护中的产品文档?

这篇文章对应的主题已经有文档中心页面。需要规范流程、当前参数和长期参考时,优先看 docs。

简介

浏览器指纹保护通常依赖于向渲染输出添加噪声:Canvas 像素、音频处理结果或 WebGL 渲染数据的细微变化。这种噪声阻止了原始设备特定指纹的捕获。然而,如果噪声是随机的,指纹在每次页面加载、每次会话和每次浏览器重启时都会改变。变化的指纹本身就是一个独特的信号,因为真实设备产生稳定、一致的输出。

BotBrowser 通过 --bot-noise-seed 标志解决了这个问题。BotBrowser 不是应用随机噪声,而是使用用户提供的值作为种子的确定性随机数生成器(RNG)。相同的种子始终产生相同的噪声模式,这意味着每次都生成相同的指纹。这使得指纹输出跨会话稳定、跨机器可重复、可预测用于测试,同时仍然与原始设备输出不同。

隐私影响

随机噪声和确定性噪声之间的区别具有重要的隐私意义。

随机噪声在每次会话中创建新的指纹身份。从追踪器的角度来看,每次访问都会改变的指纹是可疑的。真实浏览器产生稳定的指纹。Canvas 哈希在每次页面加载时都变化的用户会从绝大多数 Canvas 哈希数周或数月保持恒定的访问者中脱颖而出。这种不稳定性可能触发额外的审查和更激进的追踪技术。

确定性噪声相反,产生一个行为与真实设备完全相同的稳定指纹。Canvas 哈希在页面加载之间一致。Audio 指纹今天与昨天相同。WebGL 输出在浏览器重启之间保持稳定。从任何指纹脚本的角度来看,浏览器看起来是一个具有正常、不变指纹的正常设备。

这种稳定性对几个用例至关重要:

  • 多会话一致性: 以相同指纹返回网站看起来自然。每次以不同指纹到达暗示使用了隐私工具。
  • 账户管理: 管理多个账户时,每个账户需要自己的稳定身份。每个账户一个噪声种子确保每个身份具有一致的、独特的指纹。
  • 测试和质量保证: 验证依赖指纹行为的自动化测试需要可重复的结果。随机噪声使测试不稳定;确定性噪声使它们可靠。
  • 研究: 研究指纹的隐私研究人员需要受控条件。固定的噪声种子能够精确再现实验条件。

技术背景

确定性 RNG 工作原理

确定性随机数生成器(也称为伪随机数生成器,PRNG)产生看起来随机但完全由其初始状态(种子)决定的数字序列。给定相同的种子,PRNG 总是产生完全相同的数字序列,按相同的顺序,无论何时何地运行。

BotBrowser 使用通过 --bot-noise-seed 提供的值作为种子的加密质量 PRNG。当 Canvas 渲染需要噪声扰动时,它从此 PRNG 中提取。当 Audio 处理需要变化时,它从相同的 PRNG(或派生的)中提取。当 WebGL 输出需要修改时,相同的确定性源提供值。

关键属性是:

  • 确定性: 相同种子产生相同输出,始终如此
  • 独立性: 不同种子产生完全不相关的输出
  • 均匀性: 噪声分布看起来自然,没有模式
  • 跨平台一致性: 相同种子在任何主机操作系统上产生相同输出

什么被加种子

噪声种子控制 BotBrowser 中所有与渲染相关的变化:

  • Canvas 2D: Canvas 渲染输出中的像素级扰动。当页面调用 canvas.toDataURL()canvas.getImageData() 时,返回的数据包含从种子派生的噪声。
  • WebGL: 着色器输出、读回数据和渲染器特定变化由种子决定。
  • Audio: AudioContext 处理(振荡器输出、分析器数据、动态压缩器行为)包含种子决定的变化。
  • 字体: 字形测量微变化被加种子,确保一致的 measureText() 结果。

每个子系统使用 PRNG 输出流的一部分,确保应用于 Canvas 的噪声不干扰应用于 Audio 的噪声,同时两者对相同种子值保持确定性。

种子空间和碰撞

噪声种子是一个整数值。不同的整数值产生完全不同的指纹输出。相邻种子值之间没有可预测的关系(种子 42 和种子 43 产生完全不相关的指纹)。可能指纹的空间足够大,使得意外碰撞(两个用户巧合选择相同种子)在实践中可以忽略不计。

与配置文件的交互

噪声种子与指纹配置文件配合工作,而不是替代它。配置文件定义设备特征:屏幕分辨率、GPU 型号、平台、浏览器版本、字体和所有其他硬件/软件信号。噪声种子控制该配置文件设备身份内的渲染变化。

可以这样理解:配置文件定义呈现什么类型的设备,噪声种子决定该"实例"设备的特定渲染特征。具有相同配置文件但不同种子的两个实例看起来像同一型号的两个不同物理设备。具有相同配置文件和相同种子的两个实例看起来像同一设备。

常见保护方法及其局限性

随机噪声(无种子)

向 Canvas、WebGL 和 Audio 输出添加随机噪声的工具在浏览器每次启动时创建不同的指纹。问题包括:

  • 指纹不稳定触发追踪器怀疑
  • 自动化测试无法依赖一致的输出
  • 会话连续性不可能(每次访问看起来像新设备)
  • 噪声模式的统计分析可以揭示其人为来源

固定合成输出

一些工具用单个硬编码值替换 Canvas 或 Audio 输出。这产生稳定性但有自己的问题:

  • 工具的所有用户产生相同的指纹,创建可检测的集群
  • 固定输出可能与设备的其他信号(GPU、字体、操作系统)不匹配
  • 如果硬编码数据被发现,所有用户立即可被识别

基于扩展的噪声

注入噪声的浏览器扩展面临其他文章中讨论的限制:它们在 JavaScript API 级别操作,可以通过属性描述符检查被检测,并且无法一致地控制所有渲染路径。

无噪声(仅配置文件)

使用配置文件而不添加任何噪声会产生由主机硬件渲染管线决定的输出。虽然配置文件控制报告的值,但实际渲染仍然反映主机 GPU 和字体特征。添加种子噪声确保渲染输出是受控的而不是从主机泄露。

BotBrowser 的引擎级方法

--bot-noise-seed 标志

BotBrowser 的 --bot-noise-seed 标志接受一个整数值,为所有与渲染相关的噪声提供种子:

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

使用此配置:

  • Canvas toDataURL()getImageData() 每次返回相同数据
  • WebGL 读回操作在会话之间产生相同结果
  • Audio 处理(通过 AudioContext)生成相同的波形数据
  • 字体指标在浏览器重启之间保持一致
  • 指纹哈希(Canvas 哈希、Audio 哈希、WebGL 哈希)是稳定的

Canvas 噪声开关

BotBrowser 通过 --bot-config-noise-canvas 提供对 Canvas 噪声的精细控制:

# 启用 Canvas 噪声与确定性种子
chrome --bot-profile="/path/to/profile.enc" \
       --bot-noise-seed=42 \
       --bot-config-noise-canvas=true

# 专门禁用 Canvas 噪声
chrome --bot-profile="/path/to/profile.enc" \
       --bot-noise-seed=42 \
       --bot-config-noise-canvas=false

当 Canvas 噪声启用时,种子控制确切的扰动。当禁用时,Canvas 输出完全由配置文件和渲染引擎决定,没有额外噪声。

多身份管理

为管理多个不同身份,为每个身份分配唯一的噪声种子:

# 身份 A: 账户 A 的一致指纹
chrome --bot-profile="/profiles/profile-a.enc" \
       --bot-noise-seed=1001 \
       --user-data-dir="/data/account-a"

# 身份 B: 不同但同样一致的指纹
chrome --bot-profile="/profiles/profile-b.enc" \
       --bot-noise-seed=2002 \
       --user-data-dir="/data/account-b"

# 身份 C: 第三个不同身份
chrome --bot-profile="/profiles/profile-c.enc" \
       --bot-noise-seed=3003 \
       --user-data-dir="/data/account-c"

每个身份有自己的配置文件(定义设备特征)和自己的噪声种子(定义渲染变化)。指纹彼此不同且跨会话稳定。

跨机器可重复性

确定性噪声最强大的属性之一是跨机器可重复性。相同的配置文件和种子在任何主机上产生相同的指纹,无论:

  • 主机操作系统(Windows、macOS、Linux)
  • 主机 GPU 型号
  • 主机字体集
  • 主机屏幕分辨率
  • Docker 容器 vs. 裸机 vs. 虚拟机

这使 --bot-noise-seed 对分布式部署至关重要,其中多台机器需要呈现相同的身份,或 CI/CD 管道需要重现生产环境的确切指纹条件。

配置与使用

基本 CLI 使用

# 使用种子的确定性指纹
chrome --bot-profile="/path/to/profile.enc" \
       --bot-noise-seed=42 \
       --user-data-dir="$(mktemp -d)"

CI/CD 管道示例

# CI 中相同的配置文件和种子产生与生产环境相同的指纹
chrome --bot-profile="/profiles/production-identity.enc" \
       --bot-noise-seed=98765 \
       --bot-time-scale=1.0 \
       --bot-fps=60 \
       --headless \
       --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-noise-seed=42',
    ],
    headless: true,
  });

  const context = await browser.newContext({ viewport: null });
  const page = await context.newPage();

  // Canvas 哈希每次运行都相同
  const canvasHash = await page.evaluate(() => {
    const c = document.createElement('canvas');
    c.width = 200;
    c.height = 50;
    const ctx = c.getContext('2d');
    ctx.textBaseline = 'top';
    ctx.font = '14px Arial';
    ctx.fillStyle = '#333';
    ctx.fillText('Deterministic test', 2, 2);
    ctx.fillStyle = 'rgba(0, 50, 255, 0.5)';
    ctx.fillRect(50, 10, 100, 30);
    return c.toDataURL();
  });

  console.log('Canvas hash:', canvasHash.substring(0, 80) + '...');
  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',
      '--bot-config-noise-canvas=true',
    ],
    headless: true,
    defaultViewport: null,
  });

  const page = await browser.newPage();
  await page.goto('about:blank');

  // Audio 指纹也是确定性的
  const audioHash = await page.evaluate(() => {
    return new Promise(resolve => {
      const ctx = new OfflineAudioContext(1, 44100, 44100);
      const osc = ctx.createOscillator();
      osc.type = 'triangle';
      osc.frequency.value = 10000;
      osc.connect(ctx.destination);
      osc.start(0);
      ctx.startRendering().then(buffer => {
        const data = buffer.getChannelData(0).slice(4500, 5000);
        const sum = data.reduce((a, b) => a + Math.abs(b), 0);
        resolve(sum.toFixed(10));
      });
    });
  });

  console.log('Audio hash:', audioHash);
  await browser.close();
})();

完整确定性配置

为最大可重复性,将噪声种子与计时和帧率控制结合:

chrome --bot-profile="/path/to/profile.enc" \
       --bot-noise-seed=42 \
       --bot-time-scale=1.0 \
       --bot-fps=60 \
       --bot-config-noise-canvas=true \
       --user-data-dir="$(mktemp -d)"

验证

要验证确定性行为,使用相同配置运行两次并比较:

// 运行 1: 捕获指纹哈希
const run1Canvas = await page.evaluate(() => {
  const c = document.createElement('canvas');
  c.width = 200; c.height = 50;
  const ctx = c.getContext('2d');
  ctx.font = '14px Arial';
  ctx.fillText('Test string', 10, 25);
  return c.toDataURL();
});

// 运行 2: 相同配置文件,相同种子,应产生相同哈希
// (在运行之间重启浏览器)
const run2Canvas = await page.evaluate(() => {
  const c = document.createElement('canvas');
  c.width = 200; c.height = 50;
  const ctx = c.getContext('2d');
  ctx.font = '14px Arial';
  ctx.fillText('Test string', 10, 25);
  return c.toDataURL();
});

console.log('Canvas match:', run1Canvas === run2Canvas);
// 应打印: Canvas match: true

检查事项:

  1. Canvas 输出在使用相同种子的浏览器重启之间相同
  2. Audio 处理在会话之间产生相同的哈希值
  3. WebGL 读回数据在运行之间匹配
  4. 不同种子产生不同的(但各自稳定的)输出
  5. 不同主机上的相同种子产生相同指纹
  6. 指纹测试工具(CreepJS、BrowserLeaks)显示稳定的哈希

最佳实践

  1. 为每个身份选择唯一种子。 每个浏览器身份应有自己的噪声种子。对多个身份使用相同种子会使它们产生相同的渲染输出,这可能关联它们。

  2. 安全存储种子。 噪声种子是身份配置的一部分。将其与配置文件路径和用户数据目录一起视为敏感数据。

  3. 在 CI/CD 中使用种子。 对于自动化测试,始终指定噪声种子以确保确定性结果。没有种子,渲染输出可能在测试运行之间变化。

  4. --bot-time-scale 结合。 为了完全确定性,同时控制渲染噪声和计时信号。噪声种子处理 Canvas/Audio/WebGL,而 --bot-time-scale 处理性能计时。

  5. 记录种子分配。 在多身份部署中,维护哪个种子分配给哪个身份的映射。这确保你可以重现任何身份的确切指纹。

  6. 避免为相关身份使用连续种子。 虽然连续种子(1、2、3)产生不相关的输出,使用有意的映射(如哈希账户标识符)提供更好的组织清晰度。

常见问题

噪声种子会影响页面内容或渲染质量吗?

不会。噪声在 Canvas 的亚像素级别和 Audio 和 WebGL 的类似精度上应用。这些变化对人眼不可见且听不到。页面无论种子值如何,外观和功能都完全相同。

使用相同种子的两个用户可以被关联吗?

如果两个用户使用相同的配置文件和相同的噪声种子,他们的渲染输出将相同,理论上可以关联它们。这就是为什么每个身份应使用唯一种子。使用相同种子的不同配置文件产生不同的输出,因为配置文件也影响渲染。

没有 --bot-noise-seed 会怎样?

没有噪声种子,BotBrowser 仍然应用配置文件的设备特征,但渲染噪声(如果启用)可能使用会话随机种子。这提供与主机不同的输出,但不保证跨会话一致性。为了稳定的指纹,始终指定噪声种子。

种子需要是特定类型的数字吗?

种子是一个整数。任何整数值都有效。选择大数、质数或任何其他特定模式没有优势。只需使用每个身份唯一的值。

我可以更改种子以获得新指纹吗?

可以。在保持相同配置文件的同时更改噪声种子会产生新的、不同的指纹。当你需要轮换身份同时保持相同的设备类别(相同配置文件、不同渲染特征)时,这很有用。

--bot-noise-seed 如何与 --bot-config-noise-canvas 交互?

--bot-config-noise-canvas 切换是否应用 Canvas 噪声。--bot-noise-seed 控制应用时的噪声模式。如果 Canvas 噪声被禁用,种子仍然影响 Audio 和 WebGL 噪声。如果 Canvas 噪声被启用,种子使其确定性。

噪声种子与所有配置文件兼容吗?

是的。噪声种子独立于配置文件的设备配置。任何种子与任何配置文件配合使用。种子控制渲染变化,而配置文件控制设备身份。

指纹服务能检测到噪声是确定性的吗?

不能。确定性种子产生的噪声模式在统计上与自然硬件变化无法区分。只有当相同种子被多次测试并确认完全相同的输出时,确定性才可观察,这是预期的行为(稳定性,而非随机性)。

总结

--bot-noise-seed 标志是 BotBrowser 中可重复指纹保护的基础。通过为控制 Canvas、WebGL、Audio 和字体渲染变化的确定性 RNG 提供种子,它产生跨会话稳定、跨机器可重复、与自然设备输出无法区分的指纹。结合配置文件管理Canvas 保护Audio 指纹控制性能计时,噪声种子可重复性为隐私保护、多账户管理、测试和研究启用一致的、可预测的指纹身份。

#Noise-Seed#Deterministic#浏览器指纹识别#Reproducibility#Privacy#Ci-Cd

让 BotBrowser 从研究走向生产

先用这些指南理解模型,再进入跨平台验证、隔离上下文和面向规模化的浏览器部署。