Chromium 148 一项调整,让 `Math.tanh()` 成了新的浏览器指纹特征
一次不起眼的实现调整,暴露了浏览器底层运行环境的差异
slashslashdev·

提到浏览器指纹(Browser Fingerprinting),开发者首先想到的是 Canvas、WebGL、字体或 AudioContext 等接口。过去几年,围绕浏览器指纹的实现大多也集中在这些方向。不过,Scrapfly 最近在技术博客中分析了 Chromium 148 的一项实现变化:由于 Math.tanh() 的底层实现发生调整,同一段 JavaScript 在不同操作系统上的计算结果开始出现细微差异。这种变化几乎不会影响业务逻辑,却为浏览器指纹增加了一项新的可观测特征。
这并不是 JavaScript 语言本身发生了变化,而是 Chromium 底层实现调整带来的结果。
Chromium 148 带来了什么变化
执行下面这段代码:
console.log(Math.tanh(0.8));在 Chromium 148 上,不同操作系统(以及不同版本的底层数学库)可能得到略有不同的结果,例如:
Linux : 0.6640367702678491
macOS : 0.6640367702678490如果不把结果放在一起比较,大多数开发者甚至不会注意到这点差异。差异的只是最后一位浮点数。对于绝大多数 Web 应用来说,这种误差几乎没有影响;但对于浏览器指纹而言,它可以作为一个额外的识别特征。也正因为如此,Chromium 148 的这次实现调整才引起了关注------在此前的版本中,Math.tanh() 的计算结果一直保持跨平台一致。
为什么有这个差异
JavaScript 的 Number 使用 IEEE 754 双精度浮点数格式,但这并不意味着所有超越函数都必须在不同平台产生完全一致的最后一位结果。
对于 tanh() 这样的数学函数,不同数学库可能采用不同的数值逼近算法、舍入策略以及硬件优化(例如 Fused Multiply-Add,FMA),因此可能产生 1 ULP(Unit in the Last Place)左右的差异。根据 Scrapfly 的分析,Chromium 148 调整了 Math.tanh() 的底层实现方式,使其结果开始受到操作系统数学库的影响,这也是不同平台出现细微差异的原因。
浏览器指纹多了一个观察维度
浏览器指纹很少依赖某一个 API,而是把多个运行时信息组合在一起,例如:
User-Agent
Canvas
WebGL
Fonts
Timezone
Language
AudioContext
MathMath.tanh() 的计算结果,本质上反映的是浏览器运行时行为的一部分,因此可以作为浏览器指纹中的补充信息,而不是替代 Canvas、WebGL 等已有特征。
对自动化浏览器意味着什么
对于自动化浏览器来说,仅修改 User-Agent 或其他公开属性,并不能保证整个运行环境都保持一致。如果浏览器宣称自己运行在某个平台,但实际运行时表现更接近另一个平台,就可能暴露出前后不一致的环境特征。当然,数学计算只是众多检测信号中的一种,并不能单独判断浏览器是否经过伪装。理论上,可以通过覆盖 JavaScript 接口修改返回值,但实际还需要绕过接口完整性校验,并保证其他运行时行为保持一致,因此远没有修改一个属性那么简单。
它到底有什么价值
更适合作为一种辅助信号,而不是决定性依据。
- 差异通常只体现在最后几位浮点数;
- 当前相关讨论主要集中在
Math.tanh(),并不能延伸到所有相关数学函数; - 浏览器和底层数学库后续仍可能继续调整,因此结果并非固定不变。
从数值计算角度来看,这属于不同数学库实现带来的正常差异;但从浏览器隐私角度,它会增加新的可观测特征,因此也被认为是一类值得关注的指纹泄露问题。
带来的启示
Chromium 148 的一次底层实现调整,让原本跨平台一致的计算结果开始受到操作系统数学库影响。对开发者来说,这说明浏览器指纹正在从读取公开属性,逐渐扩展到观察运行时行为。而对于浏览器厂商来说,这类底层实现细节除了影响兼容性,也可能带来新的隐私问题,需要在性能、兼容性和隐私之间继续权衡。
参考资料
- Scrapfly:Your Browser Does Math Differently on Every OS, and Anti-Bot Systems Read the Bits