全部文章

检测与排查

User-Agent 与 Client Hints 不一致:网站怎么识破假 UA

改 UA 字符串很容易,难的是让 Client Hints、navigator.platform、字体和显卡一整套信息保持一致,多数伪装就败在这里。这篇列出每一项检查,以及怎样全部通过。

作者 Mango Team发布于 4 分钟阅读

改 User-Agent 只要一个扩展、十秒钟。macOS 上的浏览器可以自称是 Windows 上的 Chrome,请求头也会这么说。问题在于,请求头已经不是网站最先看的地方了。Chrome 现在会发送第二份结构化的自我描述,另外还有十几个属性各自独立地描述这台机器。新故事和旧故事对不上时,网站学到的不是「你在用 Windows」,而是「你的 User-Agent 是假的」。

网站有两种办法知道你是什么浏览器

User-Agent 字符串是老办法:一行文本,作为请求头发送,也通过 navigator.userAgent 暴露给脚本。从 2022 年起,Chrome 对它做了缩减:平台部分被冻结(所有 Windows 版本都是 Windows NT 10.0; Win64; x64,所有 Mac 都是 Macintosh; Intel Mac OS X 10_15_7,Apple 芯片也一样),版本只显示主版本号,比如 Chrome/152.0.0.0。

User-Agent Client Hints 是新办法。Chrome 在每个请求里发送三个低熵提示(带品牌列表的 Sec-CH-UA、Sec-CH-UA-Mobile、Sec-CH-UA-Platform);服务器通过 Accept-CH 索取时,还会发送高熵提示:平台版本、架构、位数、设备型号和完整版本列表。脚本通过 navigator.userAgentData 和 getHighEntropyValues() 也能拿到同样的数据。从字符串里拿掉的细节,都住在这里。

Firefox 和 Safari 没有实现 Client Hints。Firefox 里没有这些提示是正常的,不算不一致;一个没有它们的 Chrome 才算。

不一致从哪里来

User-Agent 说但是网站的结论
WindowsSec-CH-UA-Platform 说 "macOS"字符串被改了,浏览器没有
Chrome 152完整版本列表说是 148有人改了字符串里的数字
Chrome/152.0.6812.113Chrome 在字符串里只会发 152.0.0.0字符串是手写的
Chromenavigator.userAgentData 不存在不是 Chrome,或者是被剖开过的 Chrome
Windowsnavigator.platform 返回 MacIntel操作系统的说法是假的
Windows字体列表里有 Helvetica Neue,没有 Segoe UI操作系统的说法是假的
WindowsWebGL 报告的是 Apple 显卡操作系统的说法是假的

第一行是最常见的:扩展改写了请求头和 navigator.userAgent,其他什么都没碰。其余各行是细心的网站会做的交叉检查,大致按成本排序。没有一项需要权限,所有项都在首次加载页面时完成。

其他必须一致的属性

User-Agent 只是同一台机器至少六种描述中的一种,网站会把它们互相比对:

  1. navigator.platform。 Win32、MacIntel 或 Linux x86_64。它早于 Client Hints 出现,至今仍然必须匹配。
  2. 请求头里的 Client Hints 与 JavaScript 里的。 两者都来自浏览器,理应完全相同;不一致说明其中一个被打了补丁。
  3. 字体。 每个操作系统都自带一组核心字体(Windows 的 Segoe UI,macOS 的 Helvetica Neue 和 San Francisco),页面无需权限就能探测。
  4. WebGL 渲染器。 显卡字符串会写出厂商,往往还有只存在于某一平台的驱动栈(Windows 的 Direct3D11,macOS 的 Metal)。
  5. 语音合成的声音。 speechSynthesis 的声音列表在 Windows 上是 Microsoft 的声音,在 macOS 上是 Apple 的声音。
  6. 版本年龄。 落后三个主版本的 Chrome 在真实用户里很少见,在几个月前冻结了身份的工具里很常见。
假的 User-Agent 不会让你像另一台机器,只会让你像一台被改过的机器。

上面每一个属性描述的仍然是真实的机器。被改过的字符串是唯一与它们全部相悖的值,而这是最容易被识破的一种谎言。

怎么检查

  1. 打开实验室,看 User-Agent 和 Client Hints 两行。实验室会向 Chrome 索取高熵提示,比较请求头里的副本和 JavaScript 里的副本,再把两者和字符串比较。
  2. 接着看操作系统一行。它把 navigator.platform、字体、显卡和声音列表与 User-Agent 声称的平台交叉核对。
  3. 如果你用了 User-Agent 扩展,关掉再测一次。不一致应该消失,这说明扩展就是原因。
  4. 顺便看版本。如果字符串声称了完整的构建号,或者一个很旧的主版本,这本身就是一个标记。

检测你自己的 User-Agent 与 Client Hints

实验室比较 User-Agent 字符串、Client Hints 请求头和 JavaScript 里的值,并检查字体、显卡和平台是否描述同一个操作系统。

打开实验室

怎么修

如果你只需要真实的浏览器看起来正常: 卸掉 User-Agent 扩展。一个保持更新的原版浏览器能通过上面的每一项检查,因为所有值都来自同一个地方。

如果你需要浏览器呈现另一个身份: 改整个身份,而不是改字符串。User-Agent、请求头和 JavaScript 里的 Client Hints、navigator.platform、字体列表、显卡渲染器和声音列表,都必须描述同一个操作系统和同一个浏览器版本,而且版本要随更新保持当前。这是渲染引擎层面的改动,不是改写请求头能做到的。

无论哪种情况: 不要编造完整版本号,不要声称一个被你的字体和显卡反驳的平台,也不要在 Chromium 浏览器里禁用 navigator.userAgentData 来「隐藏」提示。缺席同样是信号。

Mango Browser 怎么处理 User-Agent

Mango 支持 Windows 和 macOS 上的 Chrome/Chromium 与 Firefox 环境,User-Agent 跟随为该环境选择的引擎、版本和操作系统。在 Chromium 环境里,请求头和 JavaScript 中的 Client Hints 共享同一个身份,所以字符串、Sec-CH-UA-Platform、完整版本列表和 navigator.userAgentData 描述的是同一个浏览器。Firefox 环境保持 Firefox 的行为,包括没有 Client Hints,这正是真实 Firefox 的样子。

操作系统身份带动其余部分:字体列表来自该系统自己的字体池,WebGL 渲染器来自该系统的真实显卡池,声音列表跟随系统,navigator.platform 和屏幕数值与之相配。给一个环境换操作系统,这些值会一起改变,这也是让本文里的交叉检查始终一致的唯一办法。

参考资料

  1. Chrome for Developers: User-Agent Client Hints
  2. MDN: Navigator.userAgentData
  3. Chromium: User-Agent Reduction

到指纹实验室看看你的浏览器暴露了什么

继续阅读