返回知识中心
指纹

确定性模式:可重现的浏览器指纹

固定噪声种子如何让 Canvas、WebGL、Audio 和文本度量的噪声在不同会话间保持可重现,以及如何验证同一种子会重复输出、换一个种子会改变输出。

BotBrowser Team

文档中心

想直接进入 指纹 文档吗?

这篇文章属于知识中心的指南库。若你要步骤化配置、参考说明和持续更新,请直接进入对应 docs 分区。

BotBrowser 可以在受控上下文中重复带种子的浏览器可见噪声,但不能让不同构建、配置文件、机器或网站决定变得相同;本文明确这种可重现性的边界。

TL;DR

需要让受控浏览器运行重复 Canvas、WebGL、Audio 和文本度量输出时,使用正数 --bot-noise-seed,并保持配置文件和构建版本一致;更换种子应视为有意改变身份。

Contents

目录

为什么需要可重现的噪声

指纹噪声有两项方向相反的任务。Canvas、WebGL、Audio 和文本测量中的细小变化,可以避免浏览器直接呈现某一台机器的原始输出。而测试或长期运行的工作流需要相反的东西:明天再运行时得到同样的输出。如果每次启动都生成新的噪声,你就无法判断结果的变化来自你的配置、被测页面,还是噪声本身。BotBrowser 可以在受控上下文中重复使用同一份带种子的配置,让多次运行保持可比;它不会让不同构建、平台或硬件上的结果完全相同。

噪声种子解决了这个矛盾。它是一个整数,用来初始化伪随机数生成器,因此噪声值的序列完全由种子决定。使用相同种子的两次启动,会对同一幅绘制内容或同一次测量施加相同的变化;换一个种子,就会施加另一种变化。输出何时应该重复、何时应该改变,由你决定。

隐私工程师和测试人员通常在四种情况下使用固定种子:

  • 会话连续性。 在多次启动之间回访同一服务,每次都呈现相同的 Canvas、WebGL、Audio 和文本度量输出。
  • 彼此不同的稳定身份。 为各自独立的账号或测试用户分配各自的噪声模式,并且在多次启动之间保持不变。
  • 配置检查。 确认某个配置文件和一组参数能产生预期输出,并确认更换种子后输出会不同。
  • 可重现的实验。 在比较两个构建、两个页面或两个配置文件时,把指纹噪声从变量中排除。

一个噪声种子驱动伪随机数生成器,由它决定 Canvas 2D、WebGL、音频和文本度量的噪声,因此相同的构建、配置文件和种子会重复相同的输出

下面的章节会沿着种子的作用路径展开:先看它影响哪些表面,再看如何选择种子值和启动参数,最后是验证流程和需要提前考虑的限制。参考资料是公开的噪声种子可重现性文档,Canvas 的行为见来源中列出的 MDN Canvas API 参考。

种子控制什么,不控制什么

--bot-noise-seed 参数作用于 BotBrowser 添加噪声的这些表面:

  • Canvas 2D。 形状、文字,以及通过 toDataURL()、toBlob() 和 getImageData() 读取的像素。
  • WebGL 和 WebGPU。 页面从绘制表面读回的渲染输出。
  • Audio。 音频处理产生的采样值,例如 OfflineAudioContext 的渲染结果。
  • 文本度量。 measureText()、getBoundingClientRect() 和 getClientRects() 返回的测量值。

对于相同的内容,种子会重复相同的噪声。请参阅密码学随机性可观测性与浏览器指纹单独记录随机输出观察。在两个会话中,用种子 42 绘制同一场景的页面会读回相同的像素。用种子 43 绘制同一场景则会读回不同但同样稳定的像素。因此验证页面应当绘制固定的场景:如果内容在两次运行之间发生变化,输出的变化就与种子无关。

种子不会触及来自配置文件的值。navigator 属性、屏幕尺寸和 user agent 字符串都从配置文件读取,无论种子是什么,每次启动都保持一致。存储、Cookie 和会话状态也是另一回事。它们位于 --user-data-dir 指定的目录,以及通过 --bot-cookies 加载的值中,所以种子既不会恢复登录状态,也不会记住上一次访问。

命令行参考中,每个噪声通道还有各自的开关:--bot-noise-canvas、--bot-noise-webgl-image、--bot-noise-audio-context、--bot-noise-client-rects 和 --bot-noise-text-rects。它们接受 true 或 false。种子决定噪声是什么样子,开关决定哪些表面会应用噪声,两者回答的是不同的问题。

种子值本身有两条规则。有效范围是 1 到 4294967295,即最大的 32 位无符号整数。值 0 的行为不同:它让噪声保持启用并使用配置文件的默认值,但不进行确定性播种,因此输出不应在会话之间重复。如果你需要可重复性,请选择一个正整数,并在每次启动时传入。

时间是单独控制的。--bot-time-seed 接受 1 到 4294967295 的整数,用于可重现的执行时间策略,0 表示禁用。--bot-time-scale 接受小于 1.0 的浮点数,用来缩放该时间策略。--bot-stack-seed 接受 profile、real 或正整数,用于栈深度行为。这些控制彼此独立:单靠噪声种子不会让时间重复,时间种子也不会改变 Canvas 输出。当测试需要渲染噪声和时间都重复时,请有意地设置两个值,并把两者都记录下来。 关于 performance.now、hardwareConcurrency 和 deviceMemory 如何组合成时间信号,以及如何验证上报的时间,请参阅 浏览器指纹与时间:performance.now、CPU 和内存信号。

为稳定的身份选择种子

理解这一组合的实用方式是:配置文件定义设备类别,种子定义该类别内的噪声模式。配置文件 A 加种子 100 是一个稳定、可重现的指纹。配置文件 A 加种子 200 与前者共用配置文件的值,但噪声模式不同。配置文件 B 加种子 100 的基础值不同,因此输出也不同,尽管种子值相同。种子永远不会让两个不同的配置文件产生相同的指纹,因为基础值仍然不同。 把确切的 --bot-profile 路径、--bot-noise-seed 整数、--user-data-dir 和 BotBrowser 构建版本与分配记录放在一起;这四个输入中任意一个变化,都会改变比较能够证明的内容。

三个会话的简单方案可以是这样:

  • 会话一:配置文件 A 加种子 100,用于第一个账号或测试用户。
  • 会话二:配置文件 A 加种子 200,用于需要保持自己噪声模式的第二个账号。
  • 会话三:配置文件 B 加种子 100,用于恰好复用了某个种子值的另一种设备类别。

几个习惯可以让这个方案长期可靠。每个种子只生成一次,并和它所属的账号或测试用例存放在一起,因为之后无法从输出反推出种子。不要用会变化的东西来推导种子,例如时间戳或会重置的计数器,否则确定性的配置会悄悄变回每次启动都不同的值。不要让需要保持不同噪声模式的会话共用一个种子。最后,把种子的分配记录和配置文件名放在同一个地方,以便日后能重建完全相同的组合。 关于如何在不产生关联的前提下为身份分配种子,以及如何在 CI 中重复运行,请参阅 噪声种子可复现性:让指纹运行结果可重复。

当一个浏览器进程需要多个身份时,按上下文设置指纹(ENT Tier3)可以让每个 BrowserContext 携带自己的 --bot-noise-seed。文档展示的做法是在创建上下文的第一个页面之前设置参数,使渲染器以正确的值启动。第二个上下文使用不同的种子,就会得到另一种但同样可重现的模式。

种子只是普通的配置值。它们不必连续,有效范围内的任何正整数都可以。它们也不是凭据:任何拥有相同构建、相同配置文件和相同种子的人都可以重现相同的噪声模式,这正是可重现配置的意义。请像对待其他测试配置一样对待它们,与整套配置一起做版本管理,并避免放在容易被误用的地方。

请记住种子的作用范围。不同的种子会在相同的配置文件值之上产生不同的噪声模式,这并不等于互不相关的浏览器。Cookie、存储、网络路由,以及使用浏览器的人或脚本的行为,都是独立的信号,种子不管理其中任何一项。

使用固定种子启动

最简单的确定性启动只需传入配置文件和种子。使用全新的用户数据目录,可以把旧的浏览器状态排除在比较之外:

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

当时间也需要重复时,加入独立的时间和栈深度控制,并为它们设置各自的值:

chromium-browser \
  --bot-profile="path/to/profile.enc" \
  --bot-noise-seed=42 \
  --bot-time-seed=42 \
  --bot-stack-seed=profile

需要保持不同身份的两个并发会话,使用不同的种子和各自独立的用户数据目录。种子和目录是不同的控制:前者决定噪声模式,后者保存 Cookie 和存储。

chromium-browser --bot-profile="path/to/profile.enc" --bot-noise-seed=1001 --user-data-dir="tmp/session-a" &
chromium-browser --bot-profile="path/to/profile.enc" --bot-noise-seed=1002 --user-data-dir="tmp/session-b" &

浏览器自动化框架会把相同的参数作为启动参数,与 BotBrowser 构建的可执行文件路径一起传入。请在启动参数中设置它们,而不是在页面打开之后再设置。种子不会改变框架驱动页面的方式。

有时你需要某些表面输出确定的结果,而其他表面使用主机自己的渲染。按表面设置的开关接受 true 或 false,被你关闭的表面不再受种子控制:

chromium-browser \
  --bot-profile="path/to/profile.enc" \
  --bot-noise-seed=42 \
  --bot-noise-canvas=false \
  --bot-noise-audio-context=false

请把最后这种写法当作诊断步骤。如果测试结果不再重复,每次关闭一个通道,就能看出是哪个表面造成的。在依赖这套配置之前,请把这些通道重新打开,并把最终的启动命令记在笔记里,以便复用同样的参数。目标是得到一条命令行,粘贴到第二个会话中就能预期有相同的表现。

验证可重现性

只有当被测页面稳定时,验证才有意义。请使用你自己控制的页面,或者你确认每次都会绘制相同场景的指纹测试页面。它应当绘制固定的 Canvas 场景、读回固定的 WebGL 场景、渲染固定的离线音频缓冲区,并测量固定的字符串。记录它报告的值,或者每个值的简短摘要,以便精确比较。每次都以相同的方式运行页面,使用相同的窗口大小和相同的加载顺序,这样两次运行之间唯一有意的差别就是种子。 关于 AudioContext 和 OfflineAudioContext 的差异如何形成音频指纹,以及如何检查音频一致性,请参阅 音频指纹详解:AudioContext 如何追踪你。

  1. 使用配置文件和 --bot-noise-seed=42 启动,加载页面,并记录 Canvas、WebGL、Audio 和文本度量的值。
  2. 关闭浏览器,用相同的配置文件和种子再次启动,并加载同一个页面。所有记录的值都应与第一次运行一致。
  3. 把种子改为 --bot-noise-seed=43,第三次运行页面。这些值应与第一次运行不同,从而确认确实是种子在决定输出。
  4. 用 --bot-noise-seed=0 启动两次。这两个会话之间的输出预期会变化,因为 0 让噪声保持启用但不做确定性播种。
  5. 把 BotBrowser 构建、配置文件、主机操作系统和完整的参数列表与结果保存在一起。

最后一步是人们最常跳过的,却正是它让日后出现的不一致容易解释。只有构建、配置文件、种子和启动参数都相同,一个结果才能与另一个结果比较。

当第二个会话与第一个不一致时,先排查简单的原因,再怀疑种子。确认配置文件的路径和版本相同,没有框架注入额外的参数,并且页面每次绘制的内容相同。比较 BotBrowser 构建、主机的目标操作系统和完整的参数列表。文档还建议在比较运行结果时保持代理、区域设置与时区以及机器负载稳定,因为即使种子没有变化,它们也可能改变页面的行为。

每当其中一个输入发生变化时,就重复这套流程:升级 BotBrowser、替换配置文件、换一台机器,或修改启动参数。每次变更后做一次简短的复查,比在长时间测试中途才发现偏差成本更低,而且因为只有一个输入发生了变化,差异的原因也更容易缩小范围。

要能够重建一个会话,每个种子只需要记录少数几个字段:

  • 种子值,以及它所属的账号或测试用例。
  • 配置文件的名称及其版本。
  • 所使用的 BotBrowser 构建。
  • 主机的目标操作系统和机器类型。
  • 完整的启动参数列表,包括任何时间种子或栈种子。
  • 验证页面报告的值,以及运行日期。

有了这份记录,升级就变成一次比较,而不是一个意外。在新构建上用保存的种子运行验证页面,并与保存的值比较。如果一致,就继续使用。如果不同,就把新值当作新的基线,记下产生它们的构建,不要为了找回旧输出而修改种子,因为发生变化的从来不是种子。

有时一个新的噪声模式正是你想要的,例如你停用了一个测试身份并启用另一个。请有意地更换种子,在同一步骤中更新记录,并保留旧条目及其构建编号。旧种子仍然是对该构建在该配置文件下输出的有效描述。

这类验证回答的是一个范围很窄的问题:这个构建在使用这个配置文件和这个种子时,是否会重复它的噪声?它并不能告诉你某个具体网站会如何对待这个指纹。请在笔记中把这两个问题分开。

需要提前考虑的限制

可重现性仅限于相同的 BotBrowser 构建、相同的配置文件和相同的 --bot-noise-seed 值。噪声算法可能在主版本之间发生变化,所以在某个主版本上一致的种子,升级之后应重新验证。不同的机器、不同的主机目标操作系统或不同的启动参数,同样可能改变结果。对跨机器或跨版本的一致性,请用上面的流程去确认,而不是想当然地认为成立。

稳定的指纹也是一种取舍。每次访问都呈现相同的输出,正是会话连续性得以成立的原因,也是让重复访问被识别为重复访问的同一属性。如果你希望相互独立的会话带有互不相关的噪声模式,就给它们不同的种子,并记住噪声模式只是服务能看到的信号之一。种子不会让指纹变得无法关联,这里的任何内容也都不是在说明某个网站会如何响应。

同样需要准确把握的是,一次通过的验证能让你说什么。它能让你说,某个构建、配置文件和种子在某台机器上重复了相同的 Canvas、WebGL、Audio 和文本度量输出,而换一个种子改变了输出。它不能让你说,同样的结果在所有机器上、每次升级之后或在所有页面上都成立。请按前一种形式书写结果,几个月后有人阅读时,这份记录仍然准确。

种子 0 值得最后再提一下,因为它很容易被误选。它让噪声保持启用并使用配置文件的默认值,在你想要变化时很有用,但它不是确定性设置。如果你的验证显示输出在多次启动之间发生变化,请先检查命令行中的种子是否为正整数,以及你的启动器或框架是否把它传递了下去。

BotBrowser 支持 --bot-noise-seed(ENT Tier2),这是一个 1 到 4294967295 的整数,用来为 Canvas、WebGL、Audio 和文本度量的噪声播种,因此在会话连续性或验证测试中,相同的配置文件和种子会重复相同的指纹,而不同的种子会给出另一个独立而稳定的身份。BotBrowser 不能保证在不同构建、不同配置文件或更改过的启动参数之间输出完全一致,不能让指纹变得无法关联,也不能让网站接受它,并且不替代存储与 Cookie 管理、代理与 IP 的选择,或网站自身的检查。

关于相关表面的背景知识,请参阅什么是浏览器指纹、Canvas 指纹、音频指纹防护,以及更简短的噪声种子可重现性概览。

来源

实践结论

每份记录都保留配置文件、正数种子、构建版本和完整启动参数。构建、配置文件、机器或参数改变后重新运行固定夹具,并把结果限定为该声明上下文中的可重现性,而不是普遍保证。

#Deterministic#Noise-Seed#Reproducibility#浏览器指纹识别#隐私

本文属于指南: 什么是浏览器指纹?原理、风险、信号与隐私边界

让 BotBrowser 从研究走向生产

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