全部文章

什么是 JA4 TLS 指纹识别,它到底能说明什么?

JA4 指纹描述的是一个客户端如何说 TLS,早在它发出任何一个标头之前。本文讲清它的构成、它为何取代 JA3、它能抓到什么,以及它在哪里不再算证据。

2026年9月3日阅读约 11 分钟

客户端发出的第一样东西不是标头,而是一个 TLS client hello:它支持的加密套件列表、它想要的扩展、它 接受的协议版本,以及它希望使用的应用层协议。这些内容都以明文抵达,早于任何请求行,早于任何 User-Agent。JA4 指纹就是这条消息的紧凑摘要,而它之所以值得关注只有一个理由:客户端并不是把它当成 关于自己的说法而选定的,它是从客户端所依赖的 TLS 库里自然落下来的。

这让它成为一种与 HTTP 层完全不同的信号。User-Agent 是客户端自己写的一句话,JA4 指纹更接近一种口音。

读懂这串字符

一个 JA4 取值由下划线分隔的三部分组成:

t13d1516h2_8daaf6152771_b186095e22b6

第一部分不查表也能读。t 表示 TCP,该位置若是 q 则表示 QUIC。13 表示 TLS 1.3。d 说明存在 SNI,也就是客户端请求的是一个域名而非直接连到裸 IP。15 是提供的加密套件数量,16 是扩展数量。 h2 是 ALPN 取值,说明这个客户端请求的是 HTTP/2。

第二部分是排序后加密套件列表的截断哈希。第三部分覆盖扩展列表与签名算法,同样先排序。GREASE 取值, 也就是浏览器刻意掺入、用来让中间设备保持规矩的占位条目,会在哈希之前被剔除。

排序听起来像细节,它却是这个格式存在的全部理由。

JA3 为什么不再管用

2017 年的前身 JA3 按字段在线路上的顺序做哈希。只要 TLS 协议栈还可预测,这套做法就没问题。2023 年 初 Chrome 上线了扩展顺序随机化,每次连接都打乱 client hello 中扩展的排列,于是同一份 Chrome 安装 开始为每个请求产生不同的 JA3 哈希。建立在 JA3 放行名单之上的规则,在一个发布周期内就从精确变成了 无用。

JA4 先排序再哈希,因此重排顺序不产生任何影响。它还把可读前缀留在哈希之外,于是不需要已知取值的数据 库,你也能区分 QUIC 连接和 TCP 连接,或者留意到某个客户端只提供了三个加密套件。JA4+ 家族的其余成员 覆盖其他层:JA4H 对应 HTTP 标头,JA4T 对应 TCP 选项,JA4S 对应服务器的响应。多数检测工作用的是 TLS 这一支。

看看这条连接的来源说明了什么

它能抓到什么

诚实的说法是:JA4 非常擅长抓住那些压根没打算融入人群的客户端。

requests 的 Python 脚本产生的 client hello,没有任何浏览器会产生。Go 在 net/http 里的默认 传输有它自己可辨认的形状。curl 也一样,老版本的 OpenSSL 也一样,多数 SDK 内置的 HTTP 客户端同样如 此。它们并没有躲藏,它们本来就是这个样子;即使请求自称是 macOS 上的 Chrome 141,指纹照样把实情说 出来。

有价值的正是这种不一致。单看一个 Go HTTP 客户端的指纹,说明不了任何坏事。可一旦它与一个自称浏览器 的 User-Agent 配在一起,就说明这个客户端对自己的描述不实,而这么做的理由多半不清白。

同样的逻辑对无头浏览器也成立,只是弱一些。真实的 Chrome 构建与由 Playwright 驱动的 Chrome 共用一套 TLS 协议栈,所以它们的 JA4 取值一致。把两者分开的线索在别处:webdriver 标志、缺失的浏览器接口、 请求的时间节奏,以及客户端有没有去取浏览器会取的样式表和字体。这正是指纹只能算一项输入而不是结论的 原因,这一点在机器人检测最有效的信号里也提到过。

它在哪里不再算证据

有两个限制值得记住,而它们在厂商文案里通常被一笔带过。

指纹标识的是群体,不是身份。 Windows 上的每一个 Chrome 141 发出的 client hello 实际上都一样, 那是数百万人共用一串字符。你可以说“这条连接来自形状像 Chrome 141 的东西”,但关于是谁,你什么都说 不了。把 JA4 取值当作访客标识,你会不断出错,而且是两个方向都会错。

指纹是可以复制的。 uTLS 能让一个 Go 程序发出与指定 Chrome 版本逐字节相同的 client hello, curl-impersonate 为 curl 做同样的事。任何有动力读完一篇文章的人,都能让自己的爬虫展示一个完全普通 的浏览器指纹。这件事的代价是投入,而投入本身就是一道真实的筛子:肯下这份功夫的脚本和不肯下的脚本之 间的差距,构成了互联网上大部分自动化流量。但认真的对手能绕过这项信号,因此任何只依赖 JA4 的方案都 会把他们放进来。

还有第三个更安静的问题。做 TLS 检查的企业代理会重写 client hello,于是它后面的所有员工共享的是代理 的指纹,而不是自己浏览器的指纹。凭指纹拦截,你可能一次性把一整家公司挡在门外。

与其他信号一起使用

真正站得住的做法是叠加,不是排名。每项信号回答的问题都不同:

信号它回答的问题何时失效
JA4这是哪套 TLS 协议栈?客户端使用 uTLS,或位于做 TLS 检查的代理之后
IP 来源连接从哪里发起?对方租用住宅地址
无头浏览器痕迹是否有人在驱动浏览器?工具链修补了自己的痕迹
标头一致性各项声称彼此吻合吗?请求是被仔细手工拼装的
反向 DNS 与 ASN这个爬虫是它自称的身份吗?营运方没有公布任何可核对的资料

把“何时失效”这一列从上到下读一遍,要点就很清楚了:每一行对应的规避手段各不相同。攻破其中一项很 容易,同时攻破五项就是一个工程。数据中心 IP 加上 Go 的指纹再加上 Chrome 的 User-Agent,毫无歧义。 住宅 IP 配上真实的 Chrome 指纹、没有无头痕迹,那多半是个人,也应该按人来对待。

Agentscan 正是按这套逻辑运行的。一次 POST 返回一个判定类别,humanknown_botai_agentmalicious_automation,附带信心值以及支撑判定的各项信号,其中包括 JA4。命中缓存的 判定在 50 毫秒内返回,这也是这项检查能放进 middleware,而不必留到第二天早上翻日志的原因。

一套可落地的策略

  1. 在 TLS 终结的位置取得指纹。 应用代码永远看不到 client hello。它来自你的 CDN、负载均衡器, 或者挡在前面的检测 API。
  2. 对不一致打分,而不是对指纹打分。 信号是“TLS 协议栈与声称的浏览器对不上”,不是“这个哈希是坏 的”。维护一份哈希黑名单会迅速过时,因为每个浏览器版本都会造出新的取值。
  3. 绝不让它单独决定。 至少要与来源配合。单独使用时,它会抓住企业代理、放过熟练的爬虫,两边都 错。
  4. 预期取值会不断变动。 浏览器更新会改变加密套件与扩展集合。你今天写死的东西需要一个复查时间, 否则它迟早会悄悄开始标记真实用户。
  5. 无论如何都记录下来。 即便不据此行动,把每个请求的 JA4 存下来,也能让下一次事件变得可读。发现 有 40,000 个请求共用一个罕见指纹,比从原始日志里翻找要快得多。

结论

JA4 告诉你一个客户端如何说 TLS。要随手伪造它,比伪造任何标头都难;要刻意伪造它,则比多数厂商愿意 承认的更容易。它是关于另一端软件形态的强信号,是关于意图的弱信号,而关于身份则完全不是信号。用它抓 住那些从没打算装成人的自动化流量,对那些确实装过的流量则把它与 IP 来源、无头浏览器痕迹叠在一起,并 且不要建立任何只靠它支撑的拦截规则。

常见问题

常见问题解答

它是一串简短字符,概括客户端建立 TLS 连接的方式:协议版本、是否发送了 SNI、提供了多少加密套件与扩展、ALPN 取值,再加上两段截断哈希,分别覆盖排序后的加密套件列表,以及排序后的扩展与签名算法列表。典型取值形如 t13d1516h2_8daaf6152771_b186095e22b6。它描述的是 TLS 协议栈,而不是屏幕前的人。

相关文章