JA4H、JA4T、JA4L 把 TLS 指纹扩展到 HTTP 请求、TCP 握手与连接延迟。本文讲清各自原理,以及层与层之间的矛盾为何最能暴露代理和机器人。
TLS 指纹识别一文介绍了 JA3 和 JA4——从单次 TLS ClientHello 中读取出的指纹。但 JA4 从来就不是孤立存在的。它的创造者 FoxIO 把它作为一个更大家族的一部分发布,这个家族名为 JA4+,其中大多数成员采集指纹的对象与 TLS 毫无关系:HTTP 请求本身、其下的 TCP 握手,甚至数据包在客户端与服务器之间往返所需的时间。每一层都被独立读取,而下文会看到,真正的威力恰恰体现在各层彼此矛盾的时候。
核心要点
- JA4+ 是一整套指纹,而不是一个指纹。 JA4 覆盖 TLS ClientHello;JA4H 为 HTTP 请求生成指纹,JA4T 为 TCP 握手生成指纹,JA4L 为连接延迟生成指纹——每一个都读取同一连接中不同的、独立的一层。
- 各层相互独立,这正是关键所在。 User-Agent 和 TLS 指纹都显示「Windows 上的 Chrome」,TCP 指纹却显示「Linux 内核」——这是教科书式的代理或反检测浏览器破绽,单看任何一层都发现不了。
- JA4T 在任何加密开始之前读取 SYN 数据包。 窗口大小、TCP 选项顺序、MSS 和窗口缩放值都来自操作系统网络栈,因此 JA4T 标识的其实是「内核」——与TCP/IP 指纹识别一文所述是同一个信号,只是 JA4T 把它整理成了一个结构化、可排序的字符串。
- 该格式刻意做到人类可读。 不同于 JA3 输出的那种不透明 MD5 哈希,每一个 JA4+ 指纹都被划分为
a_b_c的结构,其中的前缀部分凭肉眼就能读懂一部分——分析人员也可以只匹配字符串的一部分,而不必整串比对。 - 授权分为两套。 JA4 本身开源(BSD 3-Clause),FoxIO 表示不会为它申请专利。JA4H、JA4T、JA4L 等其余「+」成员正在申请专利,采用 FoxIO License 1.1 授权:学术和企业内部使用没有问题,但要把它们用在出售的产品或托管服务中,就需要购买 OEM 授权。
- 这些都不会暴露给页面 JavaScript。 JA4+ 由服务器、CDN 或网络设备读取原始数据包后计算得出——像我们的指纹检测这样的客户端工具无法显示你自己的 JA4H 或 JA4T,因为你的浏览器根本看不到自己刚刚发出的数据包。
快速回顾:JA4(TLS)
TLS 指纹识别一文已经详细讲过,这里只用三句话概括。每一次 HTTPS 连接都以一条明文 ClientHello 开始,其中列出了密码套件、扩展和椭圆曲线,其排列顺序由客户端的 TLS 库决定,而非用户。JA4 把这些值排序、剥离 GREASE 噪声后进行哈希,生成类似 t13d1517h2_8daaf6152771_cb7bf5808d99 这样的指纹——在会话之间保持稳定,前缀可读,并且能标识具体使用的 TLS 库。以下各节读取的都是同一连接中不同的层。
JA4H:为 HTTP 请求生成指纹
JA4H 读取客户端发出的实际 HTTP 请求——看的不是正文内容,而是请求本身的形态(对 HTTPS 而言,由终结 TLS 的一方读取:服务器本身,或者前面的 CDN)。根据 FoxIO 的 JA4H 规范,指纹的可读前缀依次编码:请求方法(取前两个字母并转小写,GET 为 ge,POST 为 po)、HTTP 版本(HTTP/1.1 为 11,HTTP/2 为 20)、是否带 Cookie 头(c/n)、是否带 Referer 头(r/n)、两位数的头部数量(不计 Cookie 和 Referer),以及 Accept-Language 值的前四个字符(en-US 记为 enus,缺少该头时记为 0000)。前缀之后是三段短哈希,分别对应:按发送顺序排列的头部名称、Cookie 字段名,以及 Cookie 字段名连同其取值。其中 Cookie 字段名的哈希对同一网站的所有访客通常相同,名称加取值的哈希则因用户而异。
FoxIO 公开的指纹对照表里有一个真实例子:IcedID 恶意软件投递器产生的指纹是 JA4H=ge11cn020000_9ed1ff1f7b03_cd8dafe26982。解读一下:HTTP/1.1 上的 GET 请求,带 Cookie、没有 Referer、只有另外两个头部,而且完全没有 Accept-Language(末尾的 0000)。这两点都很扎眼。真实浏览器通常会以固定的、各浏览器特有的顺序发送十个以上的头部,而头部顺序正是第二段哈希所记录的内容;FoxIO 也指出,缺少 Accept-Language 强烈表明客户端并非由真人操作的浏览器。这与 HTTP/2 指纹识别一文讨论的头部顺序信号是同一个思路,只是推广到了所有 HTTP 版本,而非局限于某一个。如果你要跨时间比对指纹,有一处最新变化值得留意:2026 年 8 月 27 日的一处修正让 FoxIO 的 Rust 实现在计算 JA4H 时排除 HTTP/2 自身的伪头部(:method、:path、:scheme、:authority),因为它们是协议强制要求的,而非客户端的选择——所以该工具在修正前后生成的 HTTP/2 指纹无法直接对上。
JA4T:为 TCP 握手生成指纹
JA4T 读取的是开启 TCP 连接的 SYN 数据包——与TCP/IP 指纹识别一文所述是同一层,只是被打包进了 FoxIO 结构化的 JA4 格式,而非 p0f 那种签名匹配方式。它把 TCP 头部中的四个值组合起来:通告的窗口大小、按照网络栈发送顺序排列的 TCP 选项(以其数字类型编号表示——2 代表 MSS,1 代表 NOP 填充,3 代表窗口缩放,4 代表 SACK-permitted,8 代表时间戳)、最大分段大小,以及窗口缩放因子。
FoxIO 给出的 Windows 11 客户端示例是 JA4T=64240_2-1-3-1-1-4_1460_8:64240 字节的窗口、按 MSS–NOP–窗口缩放–NOP–NOP–SACK-permitted 顺序排列的选项、1460 的 MSS,以及值为 8 的窗口缩放。这些都与 TLS、HTTP 或 User-Agent 头无关——它们由操作系统内核写入,那时浏览器连一个加密字节都还没发出。细节里信息量很大:FoxIO 指出,Windows 不发送时间戳选项(8),而类 Unix 系统的协议栈会发送,所以上面示例中没有 8 本身就是 Windows 的特征。MSS 字段除了反映操作系统,还反映网络路径:标准以太网 1500 字节的 MTU 对应 1460 的 MSS;更低的数值(例如 1380)意味着存在加密或隧道开销。FoxIO 还举过一个类 Unix 指纹的例子,其 MSS 为 1424,即多出 36 字节开销,可能是未加密的隧道或代理。工具层面,2026 年 8 月 27 日的一处修正让 FoxIO Rust 实现的数字字段格式与其 Wireshark、Zeek 实现保持一致——如果你要比对不同工具生成的 JA4T 字符串,这一点值得留意。
JA4L:为延迟生成指纹
JA4L 是套件中的异类——它采集的不是任何协议字段,而是握手的时间。把 TCP 三次握手的三个数据包记为 A(SYN)、B(SYN-ACK)、C(ACK),由观测点记录各自的时间。FoxIO 定义 JA4L-C =(C − B)/ 2,估算观测点到客户端的单程延迟;JA4L-S =(B − A)/ 2,估算到服务器一侧的单程延迟。两者单位均为微秒,并各自配上该方向数据包上观察到的 TTL。紧挨服务器的传感器会得到有意义的 JA4L-C 和接近零的 JA4L-S,反之亦然。由于它只读取数据包时间和 IP 头部,无论流量是否加密都能使用。
这两个数字都有物理含义。光纤中的信号不可能快过光速——FoxIO 取约每微秒 0.128 英里(约 0.2 公里),再乘以一个反映实际路由的传播延迟系数——因此延迟读数给出了对端物理距离的上限,这也是 FoxIO 把 JA4L 同时当作位置测量来介绍的原因。TTL 则提供了第二条线索:不同操作系统的初始 TTL 不同(Linux 和 macOS 通常为 64,Windows 为 128),所以观察到的 TTL 既能提示跳数,也能暗示发送方的操作系统类别——又多了一个可能悄悄与 User-Agent 矛盾的字段。
因此,JA4L 可以用来交叉验证位置声明。如果某个连接的 IP 地址定位在另一个大洲,握手却在一两毫秒内完成,那就有问题——不管 IP 数据库怎么说,完成握手的机器在物理上就在附近。反过来的情况证据力较弱,但仍有用:三层 VPN 会把客户端自己的握手数据包端到端转发出去,因此测得的延迟包含了通往真实用户的那段隐藏路程;如果读数远大于出口 IP 的位置所能解释的范围,就暗示对方比看上去离得更远。(网络拥塞也会推高延迟,所以「低到不可能」的读数才是更强的信号。)
补齐套件:JA4X、JA4SSH 及其他成员
还有几名成员虽然不在浏览器隐私读者的日常关注范围内,也值得认识一下。JA4X 为 TLS 服务器(在双向 TLS 场景中也可以是客户端)出示的 X.509 证书生成指纹——但 FoxIO 明确说明,它记录的是证书如何生成,而不是证书里的具体取值。因此,同一套工具签发的证书即使名称和密钥各不相同,也会归到同一个指纹下,这对跨攻击活动追踪恶意软件 C2 基础设施很有用。JA4SSH 把同样的思路用到 SSH 上:以滚动窗口(默认每 200 个数据包)根据数据包长度、数量和 ACK 模式概括一段加密会话,无需解密就足以区分交互式 shell、文件传输和反向 shell。其余成员覆盖服务器端和其他协议——JA4S(TLS ServerHello 响应)、JA4TS(服务器的 TCP SYN-ACK)、JA4TScan(主动式 TCP 扫描器)和 JA4D(DHCP)。这些都不直接涉及浏览器,但都遵循与 JA4H/T/L 相同的设计理念:采集客户端在结构上稳定的特征,而不管它自称是什么。
为什么各层出现分歧才是关键
本站目前讨论过的每一种指纹——Canvas、WebGL、TLS——回答的都是「这一个信号说明客户端是什么」。JA4+ 真正的贡献,是把它变成一次交叉验证:读取同一连接中的若干独立层,看它们讲述的是不是同一个故事。
举一个具体例子:某个爬虫项目跑在 Linux 服务器上,发送 Windows 版 Chrome 的 User-Agent,并用 TLS 伪装库让自己的 JA4 与 Chrome 完全一致。这足以骗过只检查 TLS 的验证。但同一台服务器的内核仍然会写下自己的 TCP SYN 数据包——JA4T 读出的是 Linux 的窗口大小、选项顺序和 MSS,而这些伪装库通常不会去动它们,因为它在操作系统网络栈之上运作。同时记录两种指纹的检测系统会看到:一个自称 Windows 上的 Chrome 的请求,走的却是一次明显来自 Linux 的 TCP 握手。这种矛盾远比单独任何一个指纹都更难修补——攻击者需要同时控制伪装库、内核 TCP 栈和 HTTP 客户端的头部顺序,少控制哪一层,破绽就会出现在哪一层。这与机器人检测技术以及 JA3/JA4 如何与 TCP/IP 指纹识别叠加中描述的原则是一致的——可观察层之间的不一致,比任何单一异常都更有力。
格式的可读性:为什么 JA4+ 字符串是可读的
JA3 的输出是一个不透明的 MD5 哈希——适合精确匹配,但对着日志行一眼看去毫无意义。FoxIO 为每一个 JA4+ 指纹都设计了刻意可读的 a_b_c 结构:一个可以明文解读的前缀(方法、版本、计数、标志位),后面跟着一个或多个短哈希,用于表达那些过于细碎、无法直接拼出来的部分。这种可读性并非装饰。FoxIO 的 README 写道,这种格式允许「只用 ab、ac 或 c」进行威胁狩猎与检测——例如,分析人员可以只匹配可读前缀加其中一段哈希,而忽略另一段。
这也带来了实际的好处:由于格式是公开规定的,而不是靠逆向工程推出来的,第三方可以构建兼容的实现。FoxIO 的 README 列出了原生支持它的工具和服务,包括 Suricata、Arkime、ntopng、Cloudflare、AWS CloudFront 与 WAF,以及 Google Cloud Armor;Wireshark 和 Zeek 则通过 FoxIO 参考仓库中维护的插件获得支持。互通的前提是每个实现都输出逐字节相同的字符串,这需要持续维护:2026 年 8 月 27 日,FoxIO 在同一天合并了两处正确性修正——Rust 实现中 JA4T 的数字字段重新与 Wireshark、Zeek 的输出对齐,JA4H 的 Rust 指纹改为排除 HTTP/2 伪头部。这提醒我们:任何关于这些指纹确切格式的描述,都只对它所依据的实现版本有效。
授权:为什么生态一分为二
JA4 本身——TLS 客户端指纹——以 BSD 3-Clause 协议发布,与 JA3 当年的条款相同,FoxIO 还声明对它没有专利主张、也不会申请专利。套件中的其余成员(JA4S、JA4H、JA4L、JA4X、JA4SSH、JA4T 等)都在申请专利,并采用 FoxIO License 1.1 授权:允许学术和企业内部使用,但要在产品或托管服务中将这些方法变现,就需要 OEM 授权。这正是一些工具和服务只实现 JA4、跳过套件其余部分的原因之一——这既是技术选择,也是授权层面的决定。
你实际能做些什么
JA4H、JA4T 和 JA4L 都无法被页面 JavaScript 读取。三者都是在流量到达时计算的——依据 TCP 头部和握手时间,以及 TLS 终结后的 HTTP 请求——计算者是网络路径上的某个环节:服务器、CDN 边缘节点或监控设备。这意味着我们自己的指纹检测工具,和所有纯客户端的指纹演示一样,无法向你展示你自己的 JA4H 或 JA4T 字符串:浏览器根本看不到操作系统刚刚替它发出的数据包。
你可以这样做:
- 实时查看你的 TLS 层 JA4。 TLS 指纹识别页面会显示我们的边缘节点从你当前连接中观察到的 TLS 版本、密码套件,以及(如可获取)JA4 哈希。
- 检查交叉验证中浏览器那一半。 我们的机器人检测会查找浏览器内的自动化痕迹和无头浏览器泄露,指纹检测则展示网站可以与网络层指纹配合使用的 Canvas、WebGL 和字体信号。
- 自己抓取其余部分。 用 Wireshark 加 FoxIO 的 JA4+ 插件记录自己的流量,就能看到 JA4T 和 JA4L;JA4H 需要明文的 HTTP 请求,因此只会出现在未加密的 HTTP 流量中,除非你同时让 Wireshark 解密 TLS。
与本站其他文章的边界
- TLS 指纹识别详解覆盖的是 ClientHello 本身的 JA3/JA4——本文假设读者已经了解这一层背景。
- TCP/IP 指纹识别一般性地讨论从 IP/TCP 头部检测操作系统,独立于 JA4T 的命名和格式。
- HTTP/2 指纹识别覆盖的是 Akamai 指纹——SETTINGS 帧、伪头部顺序——这是与 JA4H 不同的另一种 HTTP 层签名。
- HTTP/3 与 QUIC 指纹识别覆盖的是 QUIC 自身的传输参数指纹。
- 后量子 TLS 指纹识别覆盖的是 ML-KEM 密钥交换如何重塑 ClientHello 的大小和字段。
- 如何检测 User-Agent 伪装从 User-Agent 的角度讨论了同样的跨层不一致原则。
本文专门讲的是 JA4+ 家族作为一个统一套件,以及为什么把它各层放在一起读——而不是只读其中任何一层——才真正有用。
常见问题
JA4H 和 HTTP 头部顺序指纹识别是一回事吗?
关系密切,但并不完全相同。JA4H 是 FoxIO 为 HTTP 请求指纹识别制定的一套特定的、标准化的格式——一个明确定义的前缀加上若干哈希段,公开发布是为了让多个工具能够生成并比对同一个字符串。「HTTP 头部顺序指纹识别」是它所基于的更一般性的技术;其他工具和检测系统会以各自互不兼容的格式计算类似的信号。
VPN 会改变我的 JA4T 指纹吗?
部分会变,而且取决于你用的是 VPN 还是代理。全隧道 VPN(WireGuard、OpenVPN、IPsec)转发的是你自己操作系统构造的 TCP 数据包,所以 JA4T 中的窗口大小、选项顺序和窗口缩放仍然描述的是你的内核。通常会变的是 MSS:隧道封装开销会把它压到普通以太网连接的 1460 以下,FoxIO 就把 1380 这样的数值列为加密或隧道的迹象。代理则不同——它会自己与网站建立一条新的 TCP 连接,因此 JA4T 描述的是代理服务器的操作系统,而不是你的。无论哪种情况,一个看起来像隧道或数据中心 Linux 主机的 JA4T,配上一个自称普通消费级浏览器的 TLS 指纹,都属于网站如何检测 VPN 和代理中提到的信号。
在哪里能查到自己的 JA4 系列指纹?
你可以在本站的 TLS 指纹识别页面看到自己实时的 JA4(TLS)指纹,因为这一项是从你的连接在服务器端计算出来、再展示给你的。JA4H、JA4T 和 JA4L 则需要一个能读取原始数据包或完整 HTTP 请求的工具——它们不像 TLS 握手指纹那样,可以通过一个简单的客户端小组件直接展示出来。
为什么一家公司会为 JA4+ 付费授权,而不是直接用 JA3?
因为 JA4+ 的设计解决了 JA3 的实际问题——抗 GREASE 干扰、可排序且可读的输出,以及覆盖 JA3 从未触及的层——这些价值足以让安全厂商围绕它构建产品。FoxIO License 1.1 本就允许免费用于学术和企业内部;只有当公司在产品或托管服务中将 JA4+ 方法变现时,才需要 OEM 授权。JA4 本身仍采用 BSD 协议,所以只需要 TLS 层指纹的厂商根本不用申请授权。


