Skip to content

速卖通网页用 WebAudio 指纹“霸占”多点连接蓝牙耳机

约 1215 字大约 4 分钟

浏览器指纹WebAudio隐私前端安全

2026-09-23

本文为编译导读,内容基于 Matt Callaghan 于 2026/08/20 发表的英文文章,并非逐字全文翻译。 原文:AliExpress webpage keeping multipoint Bluetooth headphones active with WebAudio fingerprinting

一句话概括

打开速卖通(AliExpress)首页后,页面里的反爬 / 风控脚本会悄悄创建 WebAudio 音频图并连接到系统音频输出。虽然音量为零、根本听不到,但它让浏览器的音频通道一直处于“活跃”状态,结果导致多点连接(multipoint)蓝牙耳机无法切回手机。

现象:没放任何声音,耳机却被电脑“占住”了

作者用的是一副支持多点连接的蓝牙耳机,同时连着电脑和手机。正常情况下,电脑没声音时,手机一播放,耳机就会自动切过去。

但只要电脑浏览器里开着速卖通页面,手机的声音就是过不来;关掉这个标签页,一切恢复正常。页面上既没有视频,也没有任何可见的媒体播放。Firefox 和 Chrome 下都能复现。

排查过程

常规检查都一无所获:

  • 页面中没有 <audio> / <video> 元素在播放
  • 没有明显的媒体相关调用
  • Media Session 里也没有元数据

于是作者换了思路:对 Web Audio API 做插桩。

  1. 包装 AudioContext 构造函数,每当页面创建音频上下文时就记录下来;
  2. 包装 AudioNode.prototype.connect(),观察哪些节点最终连到了 destination(即系统扬声器输出)。

一个最小化的插桩思路大致如下(示意代码,非原文):

const NativeAC = window.AudioContext
window.AudioContext = function (...args) {
  const ctx = new NativeAC(...args)
  console.trace('[AudioContext created]', ctx)
  return ctx
}
window.AudioContext.prototype = NativeAC.prototype

const nativeConnect = AudioNode.prototype.connect
AudioNode.prototype.connect = function (target, ...rest) {
  if (target instanceof AudioDestinationNode) {
    console.trace('[connect -> destination]', this)
  }
  return nativeConnect.call(this, target, ...rest)
}

结果发现:页面空闲几秒后,会创建两个隐藏的 AudioContext,并且都连到了 destination。

元凶:阿里 AWSC 风控脚本

调用栈指向两个来自阿里 AWSC(安全 / 反滥用工具集)目录的脚本:

脚本路径版本
collina.jsassets.aliexpress-media.com/g/AWSC/uab/...1.140.0
fireyejs.jsassets.aliexpress-media.com/g/AWSC/fireyejs/...1.231.67

它们搭建的音频图是经典的 AudioContext 指纹 套路:

锯齿波 OscillatorNode
  → AnalyserNode
  → ScriptProcessorNode
  → GainNode(增益设为 0)
  → AudioContext.destination

原理简述:不同设备、系统、浏览器在处理同一段音频信号时,浮点运算结果会有细微差异,把这些输出做哈希,就能得到一个相对稳定的设备标识。

问题在于最后一步——即使增益为 0,只要连到了 destination,浏览器就认为“正在输出音频”,系统音频路径保持开启,蓝牙耳机也就一直认为电脑在播放,自然不会让位给手机。

不止音频:一整套浏览器指纹

除了 WebAudio,这两个脚本还采集了大量信息,包括:

  • Canvas 渲染结果、WebGL 能力
  • 屏幕尺寸、设备像素比(DPR)
  • 硬件参数(CPU 核数、内存等)
  • 浏览器插件列表
  • 音视频编解码格式支持情况
  • WebRTC 行为
  • 性能计时数据
  • 用户交互行为模式(鼠标、键盘等)

而且代码经过混淆,在用户还没进行任何登录、支付等敏感操作之前就已经在运行了。

解决方案:用 uBlock Origin 屏蔽

作者给出的做法是在 uBlock Origin 的「我的过滤规则」中添加两条规则,仅在速卖通域名下拦截这两个脚本:

||assets.aliexpress-media.com/g/AWSC/uab/*/collina.js$script,domain=aliexpress.com
||assets.aliexpress-media.com/g/AWSC/fireyejs/*/fireyejs.js$script,domain=aliexpress.com

作者认为屏蔽是合理的取舍:这些脚本在没有任何告知的情况下对真实硬件产生了干扰,同时收集了大量行为数据;而“静音标签页”这类常规手段对 Web Audio 的这种用法并不奏效。当然,屏蔽风控脚本也可能导致部分功能(如登录验证、下单)触发额外校验,需要自行权衡。

个人思考

这篇文章对前端开发者有几点启发:

  1. “听不见”不等于“没播放”。GainNode.gain = 0 只是静音,音频管线仍然在跑。如果只是想做离线计算,应该用 OfflineAudioContext,它不会连接真实的输出设备,也就不会产生这类副作用。
  2. 指纹脚本的副作用是可观测的。风控 / 指纹方案往往默认“用户无感”,但它们会实实在在地影响耗电、硬件状态乃至外设行为。
  3. 插桩原生 API 是排查“幽灵行为”的利器。当 DevTools 面板看不出问题时,包装构造函数和原型方法再打印调用栈,往往能快速定位到真正的调用方。