音频指纹详解: AudioContext 如何追踪你
了解 AudioContext 和 OfflineAudioContext 如何创建独特的音频指纹, 以及如何在浏览器引擎级别控制音频输出。
简介
每个网页浏览器都内置了音频处理引擎。Web Audio API, 特别是 AudioContext 和 OfflineAudioContext, 让网站能够完全通过软件创建、操作和分析音频信号。无需麦克风访问权限, 不会播放声音, 也不会出现权限提示。该 API 最初是为合法目的设计的: 游戏、音乐应用、实时音频效果。但同样的处理管线在不同设备上会产生不同的输出。追踪系统利用这种差异来生成稳定的标识符, 即音频指纹, 它可以跨会话持续存在并在删除 Cookie 后依然有效。理解其工作原理是指纹保护的第一步。
隐私影响
音频指纹是当今网站可用的最有效追踪信号之一。普林斯顿大学 WebTap 项目的研究发现, 前 100 万网站中有超过 500 个部署了音频指纹脚本。这项技术尤其令人担忧, 因为它在后台静默运行。用户不会收到任何通知、权限对话框或音频处理正在进行的视觉指示。
与 Cookie 不同, 音频指纹无法被用户清除。与 IP 地址不同, 它不会在切换网络时改变。2019 年在 IEEE 安全与隐私研讨会上发表的一项研究表明, 音频指纹在浏览器会话间保持稳定, 与仅两三个其他信号结合时, 唯一性比率可超过 95%。电子前线基金会的 Panopticlick 项目确认, 即使在常见硬件配置上, 音频处理结果也对整体浏览器唯一性有显著贡献。
下图展示跟踪方把"无声音频渲染"转换成跨站点稳定标识符的完整流程。每一步都在 JavaScript 内完成, 没有权限弹窗, 也不会发出任何声音。
采集这一步对跟踪方来说成本极低, 对访客而言完全不可感知。真正昂贵的是下游设备图谱: 任何接入同一个跟踪 SDK 的站点都会上报相同的音频和数, 这些哈希聚类后就能在不相关的域名之间形成跨站点身份。防御者要做的不是阻止 AudioContext, 而是让渲染样本稳定、真实, 并与浏览器其他身份字段保持一致。
技术背景
Web Audio API 提供了两个与指纹相关的主要接口: AudioContext 和 OfflineAudioContext。
AudioContext 管理实时音频处理。它创建一个音频图, 其中节点代表振荡、增益控制、压缩和分析等操作。每个节点处理音频采样, 输出取决于底层音频子系统。
OfflineAudioContext 将音频渲染到缓冲区中, 不产生可听输出。这是最常用于指纹采集的接口, 因为它运行快速且无声。典型的指纹采集程序创建一个 OfflineAudioContext, 将 OscillatorNode 连接到 DynamicsCompressorNode, 渲染一个短缓冲区, 然后读取结果的浮点采样数据。
为什么不同设备的输出会有差异? 原因有以下几个方面:
- 音频硬件差异。 不同的声卡和音频芯片组在浮点精度和舍入行为上存在细微的数字信号处理差异。
- 操作系统音频栈。 Windows (WASAPI, DirectSound)、macOS (CoreAudio) 和 Linux (PulseAudio, ALSA) 各自采用不同的重采样算法、缓冲区处理和混音策略。
- 浏览器引擎实现。 Chromium、Firefox 和 WebKit 各自以不同的内部精度、不同的快速路径优化和不同的编译选项实现 Web Audio 规范。
- 采样率协商。 默认采样率因操作系统和硬件而异。44100 Hz 系统产生的压缩器输出与 48000 Hz 系统不同, 即使节点和参数完全相同。
这些因素的组合意味着渲染后的音频缓冲区包含细微的数值差异。对缓冲区中的所有浮点采样求和, 或对特定范围进行哈希, 会产生一个对给定设备非常稳定而在不同设备间存在差异的值。
下表展示同一段 OfflineAudioContext 测试程序在四种常见设备类别上的实际输出。每一行都对应跟踪方数据库中的一类样本。
常见保护方法及其局限性
有几种方法试图应对音频指纹问题, 但每种都有明显的缺陷。
VPN 会改变你的 IP 地址, 但对音频处理输出没有任何影响。Web Audio API 完全在浏览器内部运行, 没有网络组件。无论通过 VPN、代理还是直接连接, 你的音频指纹都是相同的。
浏览器扩展 声称可以保护音频指纹, 通常通过拦截 JavaScript 调用并向返回的缓冲区添加随机噪声来工作。这种方法有三个问题。首先, 噪声模式本身可以被检测到: 如果网站两次调用相同的音频程序得到不同的结果, 它就知道存在噪声注入。其次, 许多扩展只拦截特定的 API 方法, 而其他方法 (如 AnalyserNode.getFloatFrequencyData) 未被修改, 造成可检测的不一致。第三, 扩展在 JavaScript 空间运行, 无法修改实际的渲染管线。
随机化 音频参数 (采样率、声道数等) 通常会破坏依赖准确音频播放的 Web 应用。音乐制作工具、视频会议和游戏音频都依赖一致的音频行为。随机值也无法通过一致性检查: 网站可以验证报告的采样率是否与实际渲染输出匹配。
Tor Browser 采取了更有原则的方法, 在所有用户间标准化音频输出。然而, 这创造了一个单一的共享指纹, 本身就成为了标识符, 表明你是 Tor 用户。
根本问题在于, 有效的音频指纹保护需要对实际渲染引擎的控制, 而不仅仅是 JavaScript API 表面。
下图把四种常见音频防御方案与现代指纹识别系统的交叉验证逻辑放在一起对比。引擎层之上的方案都会在"上报参数"和"渲染样本"之间留下可测量的不一致。
BotBrowser 的引擎级方案
BotBrowser 在源头解决音频指纹问题: Chromium 渲染引擎本身。它不是通过扩展拦截 JavaScript 调用或注入噪声, 而是修改音频的内部处理方式。
当你加载指纹配置文件时, BotBrowser 会配置音频子系统以匹配目标设备。这包括:
采样率对齐。 AudioContext.sampleRate 属性和实际渲染采样率匹配配置文件的目标平台。Windows 10 配置文件以该配置典型的采样率报告和渲染。
处理管线控制。 内部音频处理节点 (OscillatorNode、DynamicsCompressorNode、BiquadFilterNode 等) 产生与配置文件设备一致的输出。这不是简单地更改一个数字。整个处理链, 包括中间浮点精度和舍入行为, 都与目标平台对齐。
OfflineAudioContext 渲染。 OfflineAudioContext 渲染的缓冲区包含与配置文件设备产生的值匹配的采样值。因为 BotBrowser 在引擎级别控制渲染, 输出与你的实际硬件无关, 始终保持一致。
AnalyserNode 一致性。 getFloatFrequencyData、getByteFrequencyData、getFloatTimeDomainData 和 getByteTimeDomainData 等方法都返回从同一受控处理管线派生的值。不存在某个 API 返回受控输出而另一个返回原生输出的空隙。
噪声种子集成。 通过 --bot-noise-seed 标志, BotBrowser 对音频输出应用确定性变化。相同的种子总是产生相同的音频指纹, 这对于跨会话维持稳定身份很有用。不同的种子产生不同但内部一致的指纹。
这种方法的关键优势是完整性。每个音频相关的 API 表面都返回从同一受控引擎派生的值, AudioContext 报告的内容与 OfflineAudioContext 渲染的内容之间不存在不一致。
配置与使用
基本配置文件加载
最简单的配置是加载一个包含音频参数的指纹配置文件:
chrome --bot-profile="/path/to/profile.enc" \
--user-data-dir="$(mktemp -d)"
这个单一标志就能配置所有音频相关行为, 使其匹配配置文件中的设备。
使用噪声种子实现确定性音频
为了跨会话实现可复现的音频指纹:
chrome --bot-profile="/path/to/profile.enc" \
--bot-noise-seed=42 \
--user-data-dir="$(mktemp -d)"
相同的配置文件和种子组合总是产生相同的音频指纹。更改种子可获得不同但仍然一致的指纹。
Playwright 集成
const { chromium } = require('playwright');
const browser = await chromium.launch({
executablePath: '/path/to/botbrowser/chrome',
args: [
'--bot-profile=/path/to/profile.enc',
'--bot-noise-seed=42'
]
});
const page = await browser.newPage();
await page.goto('https://example.com');
Puppeteer 集成
const puppeteer = require('puppeteer');
const browser = await puppeteer.launch({
executablePath: '/path/to/botbrowser/chrome',
defaultViewport: null,
args: [
'--bot-profile=/path/to/profile.enc',
'--bot-noise-seed=42'
]
});
const page = await browser.newPage();
await page.goto('https://example.com');
禁用音频噪声
如果你需要配置文件的基础音频指纹而不需要额外的噪声变化:
chrome --bot-profile="/path/to/profile.enc" \
--bot-config-noise-audio-context=false \
--user-data-dir="$(mktemp -d)"
验证
要确认音频指纹保护正常工作, 你可以跨会话比较输出。
一致性检查。 在两个使用相同配置文件和噪声种子的独立浏览器会话中运行相同的音频指纹程序 (创建 OfflineAudioContext, 渲染缓冲区, 对采样求和)。结果应该完全一致。
跨硬件检查。 在两台不同的物理机器上使用相同的配置文件和种子运行相同的测试。音频指纹应该匹配, 确认你的真实硬件没有影响输出。
API 完整性检查。 验证 AudioContext.sampleRate、OfflineAudioContext 缓冲区输出和 AnalyserNode 频率数据都与加载的配置文件对齐。这些 API 之间的不一致将表明保护不完整。
你可以访问 BrowserLeaks 或 CreepJS 等指纹测试网站, 将你的音频指纹与加载配置文件的预期值进行比较。
下面这张验证矩阵展示同一配置文件在三台不同主机上跑出来的"成功状态"。无论底层硬件或操作系统是什么, sampleRate、压缩器样本和 AnalyserNode 哈希都保持一致。
最佳实践
- 始终使用配置文件。 不使用
--bot-profile运行 BotBrowser 意味着音频输出来自你的原生硬件。配置文件才是提供保护的关键。 - 使用
--bot-noise-seed实现稳定身份。 如果你需要跨会话保持相同的指纹, 请设置一致的种子。没有种子时, 噪声在每次启动间会变化。 - 将配置文件与使用场景匹配。 Windows 配置文件产生 Windows 典型的音频输出。如果你的代理 IP 地理位置在 macOS 常见的区域, 可以考虑使用 macOS 配置文件。
- 不要无故禁用音频噪声。
--bot-config-noise-audio-context=false标志会移除一个重要的变化层。除非有特定需要基础配置文件指纹, 否则保持启用。 - 与其他保护措施结合使用。 音频指纹只是众多信号之一。使用同时控制 Canvas、WebGL、字体和 navigator 属性的完整配置文件。
常见问题
问: 音频指纹是否需要麦克风访问权限? 答: 不需要。音频指纹使用 Web Audio API 的合成和处理能力。不涉及麦克风、权限和可听声音。它完全在软件中运行。
问: 我能否检测网站是否在运行音频指纹脚本? 答: 原则上, 你可以在 DevTools 中监控 AudioContext 和 OfflineAudioContext 的创建。实际上, 追踪脚本通常会混淆其调用。BotBrowser 无论你是否检测到脚本都能保护你。
问: 噪声种子是否影响音频播放质量? 答: 不影响。噪声种子控制的是指纹相关 API 输出中的细微数值变化。正常的音频播放 (音乐、视频、语音通话) 不受影响。
问: 使用固定噪声种子时音频指纹有多稳定? 答: 完全稳定。相同的配置文件和种子在跨会话、重启和不同机器上始终产生相同的音频指纹值。
问: 如果我对同一配置文件使用不同的噪声种子会怎样? 答: 每个种子产生一个不同的音频指纹, 但仍然是内部一致的。这对于运行需要不同但有效指纹的多个会话很有用。
问: BotBrowser 是否保护基于 AudioWorklet 的指纹? 答: 是的。AudioWorklet 在 BotBrowser 控制的同一音频处理管线中运行。Worklet 的输出从同一受控引擎内部派生。
问: 音频指纹在 headless 和有界面模式之间是否一致? 答: 是的。BotBrowser 的音频控制在引擎级别运行, 与浏览器窗口是否可见无关。
总结
音频指纹利用不同设备通过 Web Audio API 处理音频信号时的自然差异。因为它不需要权限且不产生可见效果, 它是用户最难检测或防范的追踪方法之一。BotBrowser 在 Chromium 引擎级别控制音频输出, 确保 AudioContext、OfflineAudioContext 和 AnalyserNode 都产生与加载的指纹配置文件一致的结果。结合 --bot-noise-seed 标志实现确定性输出, BotBrowser 提供完整且可验证的音频指纹保护。
相关主题请参阅 什么是浏览器指纹、WebGL 指纹保护、Canvas 指纹 和 确定性浏览器行为。