JA4指纹了解
原文来自:https://mp.weixin.qq.com/s/Vo57J6l7WEt7L2bF_EAfbA
https://xz.aliyun.com/t/14054?time__1311=mqmx9DBG0QD%3DNGNDQiiQGk0G8UAfWRi8rD&alichlgref=https%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DiCpL-H6nh6F3hHSqKNxdmugqoq3VxxR00WtgPIO6P2n4QKqCs9QZmOV5veTiylJY%26wd%3D%26eqid%3Db1fbca78000a6958000000066660296f
开源链接是:
https://github.com/FoxIO-LLC/ja4
ja4概述文章是:
https://blog.foxio.io/ja4-network-fingerprinting-9376fe9ca637
在 TLS 连接开始时,客户端会发送一个 TLS 客户端 Hello 数据包,该数据包在加密通信之前以明文形式发送。此数据包由客户端应用程序生成,用于通知服务器它支持的密码以及其首选的通信方式。因此,TLS 客户端 Hello 数据包对于每个应用程序或其底层 TLS 库都是唯一的。
无论流量是通过 TCP 还是QUIC传输,JA4 都会对客户端进行指纹识别。QUIC 是新 HTTP/3 标准使用的协议,它将 TLS 1.3 封装到 UDP 数据包中。由于 QUIC 是由 Google 开发的,如果某个组织大量使用 Google 产品,QUIC 可能占其网络流量的一半,因此捕获这一点非常重要。
JA4 还清楚地显示了 ALPN(应用层协议协商)。这表示应用程序在 TLS 协商完成后想要通信的协议。“h2”= HTTP/2,“h1”= HTTP/1.1,“dt”= DNS-over-TLS 等。可能的 ALPN 的完整列表可在此处找到。此处的“00”表示缺少 ALPN。请注意,ALPN“h2”的存在并不表示浏览器,因为许多 IoT 设备通过 HTTP/2 进行通信。但是,缺少 ALPN 可能表示客户端不是 Web 浏览器。
有关实施的更多技术细节以及原始(未散列)指纹的样子可以在github 页面上找到。
客户端发送其 TLS 客户端问候数据包后,服务器将使用其 TLS 服务器问候数据包进行响应。此数据包也以明文形式发送,根据服务器在客户端问候中选择的可用选项制定。这包括从可用选项列表中选择的一个密码,以及服务器希望设置的任何扩展。
因此,Server Hello 对于服务器应用程序和发送给它的 Client Hello 都是唯一的。不同的 Client Hello 可能会导致来自同一服务器的不同 Server Hello,因此也会导致不同的 JA4S。但是,相同的 Client Hello 始终会从该服务器应用程序产生相同的 Server Hello。例如,如果客户端发送 JA4=a_b_c 并且服务器以 JA4S=d_b_e 响应,则该服务器将始终以 d_b_e 响应 a_b_c。但是,如果另一个应用程序向同一服务器发送不同的客户端 hello,例如 JA4=x_y_z,则服务器将以不同的服务器 hello JA4S=t_y_v 响应。因此,对不同的应用程序来说,响应不同,但对同一应用程序来说,响应始终相同。我在我的JA3S 博客文章中对此进行了更详细的介绍
JA3 的结构
TLSVersion,Ciphers,Extensions,EllipticCurves,EllipticCurvePointFormats
相同与不同
相同
- 关注 TLS version
- 关注 cipher suite
- 关注 TLS extension
不同
JA4不关心的
- EllipticCurves
- EllipticCurvePointFormats
增加的
-
IP Protocol,
用于标记TCP 或 QUIC,用 t 和 q 表示 -
TLS version
不同于JA3,JA4会尝试在拓展中找到可支持的最高TLS version。
指纹中代表字符串映射关系如下:$ssl_version = [ 0x0100 => "s1", 0x0200 => "s2", 0x0300 => "s3", 0x0301 => "10", 0x0302 => "11", 0x0303 => "12", 0x0304 => "13", 0x00 => "00", ];
-
SNI (Server Name Indication)
如果该拓展类型是域名(domain)记为 ‘d’,否则为’i’ -
number of Cipher Suite
-
number of Extensisions
-
ALPN (Application-Layer Protocol Negotiation)
取应用层协议协商拓展的第一个值,如果没有则用 00表示。
具体算法如下// $tls_tls_handshake_extensions_alpn_str = ['h2','http/1.1']; h2 // $tls_tls_handshake_extensions_alpn_str = ['h3','http/1.1']; h3 // $tls_tls_handshake_extensions_alpn_str = ['http/1.1']; h1 // $tls_tls_handshake_extensions_alpn_str = ['http/1.0']; h0 $first_ALPN = current($tls_tls_handshake_extensions_alpn_str); if (strlen($first_ALPN) > 2) { $first_ALPN = substr($first_ALPN, 0, 1).substr($first_ALPN, -1, 1); } else { if (strlen($first_ALPN) > 127) { $first_ALPN = '99'; } }
-
Cipher Suite sroted
相比JA3,JA4会将Cipher Suite进行排序,同样忽略 GREASE_TABEL类型的拓展 -
Extensisions sroted
相比JA3,JA4会将Extensisions进行排序,忽略 GREASE_TABEL类型的拓展,同时会再忽略JA4_a中的SNI,ALPN拓展 -
Signature Algorithms,in the order they appear
签名哈希算法(准确的说:拓展signature_algorithms(13),按出现的顺序。注意:在firefox中,会有拓展delegated_credential(34),也是一个提供Signature Algorithms的拓展,但计算JA4时应该忽略,具体看JA4 is including signature hash algorithms outside of the signature hash extension
常见的JA4
brower | JA4 | mark |
---|---|---|
Chrome/121 | t13d1516h2_8daaf6152771_02713d6af862 | |
Chrome/121 | t13d1517h2_8daaf6152771_b1ff8ab2d16f | no key |
Chrome/121 | 13d1517h2_8daaf6152771_b0da82dd1658 | with pre_share_key |
Edg/122 | t13d1516h2_8daaf6152771_02713d6af862 | |
Edg/122 | t13d1517h2_8daaf6152771_b0da82dd1658 | with pre_share_key |
Firefox/123.0 | 13d1715h2_5b57614c22b0_5c2c66f702b0 | |
Firefox/123.0 | 13d1715h2_5b57614c22b0_7121afd63204 | with pre_share_key |
OPR/102 | t13d1516h2_8daaf6152771_e5627efa2ab1 | |
OPR/102 | t13d1516h2_8daaf6152771_9b887d9acb53 | with pre_share_key |
curl/7.68.0(ubuntu) | t13d3113h2_e8f1e7e78f70_ce5650b735ce | |
curl/8.4.0(win10) | t12d2109h1_76e208dd3e22_7af1ed941c26 | |
iPhone OS 16_4_1 Safari | 13d2014h2_a09f3c656075_14788d8d241b |
什么是 QUIC
对于用户侧来说 QUIC 主要解决的是延迟的问题,这里我们主要说一下它和 TCP 的区别,简单来说,QUIC 进行了一次 “资源整合”,它把之前 HTTP/2、TLS、TCP 三者的优势集合起来
由于 QUIC 已经整合了这些内容,那么让它继续工作在 HTTP/2 下会出现许多问题,所以便催生了 HTTP/3,因为 QUIC 本身是要取代 TCP 的,所以它的传输层协议选择的是 UDP
JA4 计算原理
JA4 计算的是 Client Hello 报文,这里以官方给出的 Chrome JA4 指纹作为样例来进行演示JA4=t13d1516h2_8daaf6152771_02713d6af862
,分成 JA4_a、JA4_b、JA4_c 三部分,计算的大原则是:按照规定顺序排列,忽略所有 GREASE 值
JA4_a
t
:代表 TCP,此位置代表协议,还有q
,代表 QUIC13
:代表 TLS Version,这里代表 1.3,更多情况请见下表0x0304 = TLS 1.3 = “13” 0x0303 = TLS 1.2 = “12” 0x0302 = TLS 1.1 = “11” 0x0301 = TLS 1.0 = “10” 0x0300 = SSL 3.0 = “s3” 0x0200 = SSL 2.0 = “s2” 0x0100 = SSL 1.0 = “s1” Unknown = “00”
当我们仔细观察抓到的包时,可以发现有些包 WireShark 显示的是 TLS 1.2 但 JA4_a 中给出的却是 13,这并不是计算上的错误,是因为 JA4 计算时实际上读取的是数据包中的最高版本
出现 TLS Version 的部分一共有三个部分,上面出现 13 的原因就是在拓展(Extension)中有一项supported_version
存在 TLS 1.3
d
:代表 Domain,这里的判断依据是 SNI Extensions,如果它存在则访问的是域名,不存在则说明访问的是 IP,此时为i
15
:代表 Cipher Suites 的数量,即便是单数也选择两位输出,例如 6 个就是06
,最大值显示到99
,忽略 GREASE 值
h2
:代表 ALPN 值,选取第一个值,更多情况见官方文档,若报文中没有此项,则使用00
ALPN 主要负责应用层协议的协商,浏览器发送 Client Hello 报文时同时提供两种选项,若服务器支持 HTTP/2 则选择 h2,如果不支持,则从客户端支持的协议列表中选取一个它支持的协议,一般情况下选择 HTTP/1.1,体现在 JA4 指纹中就是h1
JA4_b
JA4_b 的计算根据是 Cipher Suite 值,使用,
分隔不同字段(忽略 GREASE 值),但是会进行一个从小到大的排序,例如
1301,1302,1303,c02b,c02f,c02c,c030,cca9,cca8,c013,c014,009c,009d,002f,0035
按照从小到大的排序原则处理后,变为
002f,0035,009c,009d,1301,1302,1303,c013,c014,c02b,c02c,c02f,c030,cca8,cca9
对这串数据再进行 SHA256 加密,最终截取加密后字符串(小写)的前 12 位,组成 JA4_b
8daaf6152771e33e12d734f9bc6478ed341f16cde27aee3aa36f2402f2c53b44
JA4_b = 8daaf6152771
这里排序的意义在于降低 “Cipher Stunting“ 的影响,关于什么是 “Cipher Stunting“ 请见【JA3 优化】部分,但是并不是说顺序不重要,这里是为了保真度所做的取舍,作者给出的原因是,在研究时发现大多数应用使用的是独特的 Cipher,而不是独特的 Cipher Suite 排序(It does but in our research we’ve found that applications and libraries choose a unique cipher list more than unique ordering)
JA4_c
JA4_c 的计算根据是 Extensions-Type 值,忽略 GREASE 值、SNI 扩展名(0000)、ALPN 扩展名(0010),这里使用的是 Extensions-Type 值的十六进制格式(不含0x
)
例如以下原始数据
001b,0000,0033,0010,4469,0017,002d,000d,0005,0023,0012,002b,ff01,000b,000a,0015
经过排序后(从小到大,同时去除0000
和0010
)
0005,000a,000b,000d,0012,0015,0017,001b,0023,002b,002d,0033,4469,ff01
这里的排序目的和 JA4_b 类似,但主要对抗的是 Extension 随机化问题,例如 Google 在 2023 年更新了 Chrome,主要实现的就是随机化 Extension,避免开发者过于依赖此功能
之后会再读取一个 Extension 中 signature_algorithms(签名算法) 的值,同样使用,
作为分隔符
这里直接使用默认排序
0403,0804,0401,0503,0805,0501,0806,0601
最后将两串字符串合二为一,中间使用_
分隔,处理后的 Extensions-Type 在前,未处理的 signature_algorithms 在后
0005,000a,000b,000d,0012,0015,0017,001b,0023,002b,002d,0033,4469,ff01_0403,0804,0401,0503,0805,0501,0806,0601
对这串数据再进行 SHA256 加密,最终截取加密后字符串(小写)的前 12 位,组成 JA4_c
e5627efa2ab19723084c1033a96c694a45826ab5a460d2d3fd5ffcfe97161c95
JA4_c = e5627efa2ab1