详解 TLS 指纹与 JA4 哈希
为什么 TLS Client Hello 消息是设备识别的宝库,以及 JA4 哈希如何提供一种能在浏览器更新后依然稳定的指纹。
每一次 HTTPS 连接都以一次 TLS 握手开始,而每一次 TLS 握手都以一条 Client Hello 消息开始。这条消息包含了关于连接客户端的大量信息——密码套件、扩展、支持的曲线、签名算法——这些信息在不同浏览器、不同版本和不同操作系统之间差异显著。TLS 指纹识别正是捕获这些信息,并将其用作一种识别信号。
Client Hello 里都有什么?
当浏览器连接到一台 HTTPS 服务器时,它会发送一条 Client Hello 消息,其中包含:它所支持的 TLS 版本、它愿意使用的密码套件列表、它所包含的 TLS 扩展(如 SNI、ALPN 和 key share)、它支持的椭圆曲线、它接受的签名算法,以及它提供的压缩方法。
每一个浏览器家族都有独特的指纹。Chrome、Firefox 和 Safari 发送的密码套件排序各不相同、扩展集合各不相同、曲线偏好也各不相同。即便在同一个浏览器家族内部,随着密码套件的增加或弃用,不同版本发送的 Client Hello 消息也可能略有差异。
从 JA3 到 JA4
JA3 是最初的 TLS 指纹哈希,由 Salesforce 于 2017 年提出。它将 TLS 版本、密码套件、扩展、椭圆曲线和 EC 点格式拼接成一个字符串,再计算出一个 MD5 哈希。JA3 虽然具有开创性,但也有局限:它产生的是单一且不透明的哈希,难以分析,而且任何字段的细微变化都会得到一个完全不同的哈希。
JA4 由 FoxIO 于 2023 年提出,在多个方面对 JA3 做了改进。它产生一个由三部分组成的结构化指纹:一个可读的前缀(如 “t13d1715h2”——TLS 1.3、17 个密码套件、15 个扩展、HTTP/2)、一个密码套件的有序哈希,以及一个扩展的有序哈希。这种结构让 JA4 指纹一眼就能看懂,同时保留了识别所需的精确度。
为什么 TLS 指纹对设备情报很重要
TLS 指纹的价值在于,它在任何 JavaScript 执行之前就被采集了。一个伪装了 user agent、伪造了 canvas 渲染、篡改了 navigator 属性的机器人,仍然会从它实际使用的 TLS 库发出一条真实的 Client Hello 消息。如果 Client Hello 说的是“Go 的 crypto/tls 库”,而 user agent 说的是“Chrome 124”,我们就知道有东西被伪装了。
这种交叉验证对于机器人检测极为强大。大多数自动化框架——Selenium、Puppeteer、Playwright——都使用浏览器原生的 TLS 栈,因此它们的 TLS 指纹与其所控制的浏览器相匹配。但自定义 HTTP 客户端、基于 Go 的爬虫,以及使用 requests 库的 Python 脚本,都有各自独特的 TLS 指纹,能立刻把它们识别为非浏览器客户端。
TLS 指纹的稳定性
TLS 指纹识别的一个顾虑是它在浏览器更新后的稳定性。当 Chrome 增加或移除一个密码套件时,TLS 指纹就会变化。但在实践中,这种情况发生的频率比你想象的要低。Chrome 的密码套件列表相对稳定——重大变化每年只发生一两次,而不是每个版本都变。
JA4 的结构化格式在这里很有帮助。可读前缀在小版本变更中保持稳定(密码套件和扩展的数量不常变化),因此即便详细哈希发生了变化,前缀也能提供连续性。在我们的多层识别系统中,TLS 指纹数据被放在第二层(Tier 2)——足够稳定,可以为识别做出贡献,但会经过跨会话匹配处理,以应对预期中的漂移。
服务端采集
与需要执行 JavaScript 的客户端信号不同,TLS 指纹完全在服务端采集。我们的边缘服务器会检查原始的 TLS 握手,并在连接建立之前提取出 Client Hello。这意味着即便 JavaScript 被禁用、浏览器安装了隐私扩展,或者客户端根本不是浏览器,TLS 指纹识别也依然有效。
这种服务端的特性也使得 TLS 指纹难以被伪装。虽然理论上可以精心构造一条模仿特定浏览器的自定义 TLS Client Hello,但要做到这一点需要在底层实现 TLS——这远比改一个 user agent 字符串费力得多。大多数伪装工具并不会去尝试。
与设备识别的集成
在我们的设备识别引擎中,TLS 指纹既是一个信号,也是一个校验器。作为信号,它以自身的识别权重贡献于整体设备指纹。作为校验器,它提供了针对所声称浏览器身份的一次交叉核对。如果 JavaScript 信号说的是“macOS 上的 Chrome”,而 TLS 指纹说的是“Linux 上的 Firefox”,这种不一致就会在我们的 Smart Signals 分析中触发一个篡改标记。