-
-
[原创]TLS 特征
-
发表于: 2026-3-5 09:53 872
-
前言
JA3,JA4 其实本质上就是用来区分脚本, 浏览器请求的。 最优的解决方法就是趋同,化作水融入大海,让我们的采集脚本尽可能的像chrome 。
JA3
背景:
发起 TLS 会话,客户端会在 TCP 三次握手后发送一个 Client Hello 数据包(明文的)。不同的客户端(比如 Chrome 浏览器、Python 的 requests 库、Go 语言程序、或者某种特定的木马病毒)在底层使用的 TLS 库和配置各不相同。因此,它们在 ClientHello 中声明的“自己支持的加密算法、TLS 版本、扩展功能”等信息也会有显著差异。 如果服务器接受 TLS 连接,则会根据服务器端库和配置以及 Client Hello 数据包中的详细信息,回复一个 TLS Server Hello 数据包。利用 TLS Client Hello 数据包中的详细信息, 生成一个唯一的MD5, 来识别客户端应用程序。
原理
- 0x00: uncompressed (未压缩) - 最常用
- 0x01: ansiX962_compressed_prime
- 0x02: ansiX962_compressed_char2
- 99%的实现只支持
uncompressed - 压缩格式节省带宽但增加计算
- 实际上是
supported_groups扩展(0x000a)的内容 - 在TLS 1.3中改名为supported_groups(包含更多类型)
- 客户端支持的加密算法列表,按优先级排序
格式示例:TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- ECDHE: 密钥交换算法(椭圆曲线DH)
- RSA: 认证算法(服务器证书类型)
- AES_256_GCM: 加密算法和模式
- SHA384: 消息认证码算法
含义拆解:
TLSVersion(TLS版本):
Ciphers Suites(密码套件):
Extensions(扩展列表)
EllipticCurves(椭圆曲线)
EllipticCurvePointFormats(椭圆曲线点格式)
支持的曲线点格式
格式类型:
实际使用:
客户端支持的椭圆曲线算法列表,用于ECDHE密钥交换
# NIST曲线 23 (0x0017) = secp256r1 (P-256) 24 (0x0018) = secp384r1 (P-384) 25 (0x0019) = secp521r1 (P-521) # 现代曲线(性能更好) 29 (0x001d) = x25519 30 (0x001e) = x448 # 其他 256 (0x0100) = ffdhe2048 (DH组,TLS 1.3) 257 (0x0101) = ffdhe3072
客户端请求的 TLS 扩展功能列表,增强协议能力:
{
"server_name": "example.com", // SNI-服务器名称指示
"status_request": "OCSP", // 证书状态请求
"supported_groups": [...], // 支持的椭圆曲线组
"signature_algorithms": [...], // 签名算法
"application_layer_protocol": [...], // ALPN-应用层协议协商
"encrypt_then_mac": true, // 先加密后MAC
"extended_master_secret": true // 扩展主密钥
}客户端支持的最高 TLS 版本(如 TLS 1.2, TLS 1.3)
生成过程
提取出这 5 个字段后,JA3 会按照固定的格式将它们拼接成一个字符串,并计算哈希值:
- 拼接规则:字段之间用逗号
,隔开;字段内部的多个值之间用连字符-隔开。 - 字符串格式:
TLSVersion,Ciphers,Extensions,EllipticCurves,EllipticCurvePointFormats - 举个例子,一个拼接好的原始字符串可能长这样:
769,47-53-5-10-49161-49162-49171-49172-50-56-19-4,0-10-11,23-24-25,0 - 生成 MD5:为了方便存储和比对,JA3 会对这串长字符串计算 MD5 哈希值。
最终生成的 JA3 指纹 就是一个 32 位的字符串,例如:
cd08e31494f9531f560d64c695473da9
爬虫检测&应对
- 模拟chrome 增加GREASE , 随机打乱扩展列表顺序。在密码套件,扩展列表,椭圆曲线,椭圆曲线点格式中增加 保留的固定格式随机值。
- 像在python 中使用的request,scrapy 这类Ja3 都是固定的。
- 它的底层原理是因为没有 GREASE 机制,当发起请求时,真正构建
ClientHello握手包、决定密码套件和扩展列表的,是你操作系统上的 OpenSSL 库以及ssl模块的默认配置。它们向OpenSSL 传递一个固定写死的、按特定优先级排序的密码套件列表(Cipher Suites)和支持的椭圆曲线列表。 GREASE 机制
- 它会在
ClientHello的密码套件和扩展列表中随机插入无效值。这意味着,同一个 Chrome 浏览器,每次发起请求时,其底层的 JA3 原始字符串都在变化,从而导致生成的 JA3 MD5 指纹每次都不一样! - 预留特殊值: IETF 在 TLS 的各个参数空间(如密码套件、扩展 ID、支持的椭圆曲线组等)中,专门保留了一批永远不会被正式分配的“无效值”(例如以
0x?A?A, eg:0x1A1A等结尾的值)。 - 随机发送未知扩展: Chrome 浏览器在发起 HTTPS 连接(发送 TLS
ClientHello消息)时,会故意且随机地将这些预留的“无效值”塞进密码套件列表或扩展列表中。 - 核心原理和目的是防止网络协议(特别是 TLS 协议)发生“僵化”,TLS 规范明确要求:如果服务器或网络中间设备(如防火墙、代理服务器、负载均衡器)在握手时遇到了不认识的协议版本、密码套件(Cipher Suite)或扩展(Extension),它们应该直接忽略这些未知内容,并继续使用已知的参数完成握手。
操作:
结果:
爬虫检测
解决方式:
from curl_cffi import requests
rs = requests.get("9b2K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6@1L8s2y4Q4x3X3g2T1M7X3!0%4M7$3g2J5L8r3g2S2K9%4y4Q4x3X3g2U0L8$3#2Q4x3V1k6B7M7$3!0F1i4K6t1$3M7i4g2G2N6q4)9K6b7W2)9J5b7$3W2E0M7r3g2J5M7$3!0F1j5i4c8W2i4K6y4p5i4K6t1$3M7i4g2G2N6q4)9K6b7X3y4Z5M7X3!0E0k6g2)9J5y4Y4q4#2L8%4c8Q4x3@1u0Q4x3U0V1`.
print(rs.text)JA4+
背景
- 缺乏可读性
- 易失效:针对TLS 扩展排列随机化,GREASE 导致JA3 失真
原理
- 假设一个黑客编写了恶意扫描器,为了逃避检测,扫描器每次请求都会随机更改一个密码套件。
- 在过去,这会产生无数个完全不同的 JA3 指纹,无法追踪。
- 但在 JA4 中,因为只有密码套件变了,所以只有 Part B 会变。Part A 和 Part C 依然是固定的。安全人员只需要写一条规则:“拦截所有 JA4 指纹为
t13d1516h2_*_e5627efa2ab1的流量”(即匹配a_c),就能精准封杀这个不断变形的恶意软件。 明文元数据(人类可读):
密码套件哈希(排序 + 截断+过滤)
扩展与签名算法哈希(排序 + 截断+过滤)
t= TCP (如果是 QUIC 则是 q)13= TLS 1.3d= 包含域名 SNI (如果是 IP 则是 i)15= 提供了 15 个密码套件16= 提供了 16 个 TLS 扩展h2= ALPN 协商为 HTTP/2- 这一部分不使用哈希,而是直接提取网络请求中最关键的特征,拼接成一个简短的字符串,让分析师“一眼看懂”。
- 以 JA4(TLS 客户端指纹) 为例,Part A 的格式为:
[协议][TLS版本][SNI][密码套件数量][扩展数量][ALPN] 示例:
t13d1516h2- 提取 TLS ClientHello 中的密码套件(Cipher Suites)列表。
- 核心动作: 先将这些套件按十六进制值进行排序,然后再计算 SHA256 哈希,最后只截取前 12 个字符,期间会忽视过滤GREASE 值。
- 原理优势: 排序保证了即使客户端打乱了密码套件的发送顺序,最终生成的哈希也是一致的。截取 12 位是为了保持指纹的简短。
- 提取 TLS 扩展(Extensions)和签名算法(Signature Algorithms)。
- 核心动作: 同样先进行排序,计算 SHA256 哈希,截取前 12 个字符。
- 原理优势: 这一步完美破解了现代浏览器的“TLS 扩展随机化”防御。因为无论浏览器怎么打乱扩展顺序,JA4 在计算前都会将其重新排序,从而保证同一个浏览器生成的 Part C 永远是稳定的。期间会忽视过滤GREASE 值。
- 最终生成的 JA4 指纹看起来像这样:
t13d1516h2_8daaf6152771_e5627efa2ab1 - 解决上述问题,JA4+ 放弃了单一的全局哈希,而是采用了一种“明文元数据 + 排序分段哈希”的创新结构(a_b_c 模块化结构)。
所有的JA4+ 指纹都遵循a_b_c 的三段式格式:
匹配原理
采用了 a_b_c 的分段格式,JA4+ 进行模糊匹配或局部追踪。
JA4+ 家族矩阵
- 推测操作系统:不同的操作系统有不同的默认初始 TTL 值。Windows 默认是
128,Linux/macOS/Android/iOS 默认是64,而思科等网络设备通常是255。 - 计算路由跳数(Hop Count):如果收到的 TTL 是
42,由于它最接近64,我们可以推断初始 TTL 是64(Linux/Mac),并且该数据包经过了64 - 42 = 22个路由节点(Hops)。 Chrome
- Python
requests - scrapy
- Android
- iOS / macOS
- Java
- go
ge:HTTP 请求方法。ge代表GET(如果是 POST 请求则是po,PUT 则是pu等)。20:HTTP 协议版本。20代表 HTTP/2.0(如果是 HTTP/1.1 则是11)。c:Cookie 状态。c代表请求中包含 Cookie(如果没有 Cookie 则为n)。r:Referer 状态。r代表请求中包含 Referer 头(如果没有则为n)。13:请求头的总数量。这里表示该请求共有 13 个 HTTP Headers。enus:Accept-Language请求头的前 4 个字母(去除非字母数字字符)。例如en-US会变成enus。如果请求中完全没有Accept-Language头(常见于自动化脚本或恶意软件),这里会显示为0000。- 核心概念: JA4TScan 是一种主动的 TCP 服务端指纹扫描工具(通常结合 Zmap 等扫描器使用)。它的目的是探测并识别目标服务器的真实身份和网络架构。
工作原理:
核心应用场景:
- JA4TScan 向目标服务器发送一个 TCP SYN 包。
- 服务器收到后,会回复一个 SYN-ACK 包。JA4TScan 会记录这个包里的 TCP 选项和 MSS。
- 最巧妙的一步:JA4TScan 故意不回复最后的 ACK 包(不完成三次握手)。
- 服务器等不到回应,就会触发超时重传机制,连续多次重新发送 SYN-ACK 包。JA4TScan 会精准记录每一次重传包之间的时间间隔(秒数)。
- 精准识别服务器 OS:不同的操作系统内核,其 TCP 重传的等待时间是硬编码且完全不同的(例如 Linux 可能是 1s, 2s, 4s, 8s,而 Windows 可能是 3s, 6s, 12s)。通过测量重传延迟,可以 100% 确定服务器的底层系统。
- 看透网络拓扑(识别负载均衡/WAF):如果一个网站宣称自己是自建机房,但 JA4TScan 发现它的 MSS 是
8961,安全人员立刻就能知道它其实部署在 AWS(亚马逊云)上,因为这是 AWS 基础设施特有的 MSS 大小。它还能探测出目标是否隐藏在反向代理或端口转发之后。 - 核心概念: A4T 是一种被动的 TCP 客户端指纹识别技术。它通过分析客户端发起的第一个 TCP SYN 数据包 来生成指纹。
工作原理:当客户端(如浏览器、爬虫、扫描器)与服务器建立 TCP 连接时,会发送一个 SYN 包。JA4T 会提取这个包中的 4 个关键网络参数:
核心应用场景:
- TCP Window Size (窗口大小)
- TCP Options (TCP 选项的顺序和类型)
- MSS (最大报文段长度)
- Window Scaling (窗口缩放因子)
这四个参数组合起来,形成类似
65535_2-4-8-1-3_1412_8这样的指纹。 - 识别真实操作系统:不同的操作系统(Windows, Linux, macOS, iOS)底层网络栈对 TCP SYN 包的默认配置是完全不同的。即使黑客在 HTTP 层伪造了
User-Agent: Windows,JA4T 也能在 TCP 层看穿它其实是一台 Linux 机器。 - 检测 VPN、代理和隧道:当流量经过 VPN 或代理时,由于网络封装的开销,MSS(最大报文段长度)通常会变小(例如从标准的 1460 变成 1412 或 1380)。JA4T 可以借此精准发现隐藏在代理背后的流量。
- 秒杀全网扫描器:像
Masscan、ZMap这样的黑客批量扫描工具,为了追求极速,通常会使用自己精简的 TCP/IP 栈,它们发出的 SYN 包极其简陋(例如没有 TCP 选项,没有 MSS)。防守方只需封禁这些特定的 JA4T 指纹,就能瞬间屏蔽互联网上 80% 以上的恶意扫描流量。 - 核心概念:JA4SSH 专门用于对 SSH(安全外壳协议)流量进行指纹识别。
- 工作原理:与识别 SSH 客户端软件本身(如 HASSH 指纹)不同,JA4SSH 侧重于对 SSH 会话(Session) 的行为进行指纹提取。它通过观察 SSH 数据包的特征,以滚动的方式(默认每 200 个数据包计算一次)生成一个简单易读的指纹。
- 核心概念:JA4X 的全称是 JA4X509,专门用于对 服务器下发的X.509 TLS 证书进行指纹识别。
工作原理:传统的证书哈希(如 SHA-256)是对整个证书内容进行计算,只要证书内容变了一个字母,哈希值就会完全改变。而 JA4X 的目的不是识别证书的具体内容,而是识别证书是如何生成的。它提取证书的三个核心结构部分:
- a 部分:颁发者(Issuer RDN)的排序键的 SHA-256 哈希的前 12 个字符。
- b 部分:主题(Subject RDN)的排序键的 SHA-256 哈希的前 12 个字符。
- c 部分:证书扩展(Extensions)的排序键的 SHA-256 哈希的前 12 个字符。
这三部分拼接在一起构成了
a_b_c格式的 JA4X 指纹。 核心概念:JA4L 不看数据包里的内容,而是测量客户端与服务器之间的物理距离(基于光速和网络延迟)。
工作原理:
应用 (以下都可能会误判需要结合使用)
- JA4L (或 JA4L-C):客户端到服务器的延迟指纹。
- JA4LS (或 JA4L-S):服务器到客户端的延迟指纹。
- 含义:这是 TCP 握手完成之后,应用层协议协商(如发送 TLS ClientHello)的单向延迟。
- 作用:对比
JA4L_a(纯网络延迟)和JA4L_c(应用层延迟)。如果两者差异巨大,说明客户端在处理应用层数据时存在严重的性能瓶颈,或者流量经过了某些深度包检测(DPI)防火墙或复杂的代理网关。 - 含义:这是从 IP 数据包头中提取的 TTL 值。
- 生成逻辑:直接读取数据包到达时的 TTL 值。
作用:
- 含义:这是在 TCP 三次握手期间测量出的单向网络延迟(One-way latency),单位通常是**微秒(µs)**或毫秒(ms)。
- 生成逻辑:通过计算数据包往返时间(RTT)并除以 2 得出。例如,服务器发送
SYN-ACK到收到客户端ACK的时间差。 - 作用:光在光纤中的传播速度是固定的。通过这个纯粹的网络延迟,安全分析师可以估算出客户端距离服务器的真实物理距离(例如:延迟 33ms 大约对应几千公里的物理距离)。
JA4L_a(33) —— TCP 握手内的单向延迟JA4L_b(128) —— 观察到的 TTL(Time to Live)JA4L_c(45) —— 握手外的应用层延迟- 发现异常:如果
User-Agent说是 Windows,但JA4L_b(TTL) 却是50(推测初始值为 64,属于 Linux),这种底层网络特征与上层应用特征的矛盾,能立刻暴露出这是一个运行在 Linux 服务器上的伪造爬虫或黑客脚本。 - 通过盗取了受害者的浏览器 Cookie(Session Token),并在自己的电脑上重放这些 Cookie 来免密登录。
- 场景:受害者家里办公,其正常的指纹组合是:Cookie (
JA4H_d) 正常,操作系统是 Windows (JA4L_b接近 128),延迟 (JA4L_a) 稳定在20ms。 - 发现异常:突然,同一个 Cookie (
JA4H_d) 发起了请求,但JA4L_b变成了53(推测初始为 64,说明变成了 Linux 系统),且延迟JA4L_a变成了200ms。 - 结论:虽然身份凭证是对的,但物理位置和操作系统全变了。系统可以立刻判定 Cookie 被盗用,强制踢下线并要求重新进行 MFA 认证。
- 场景:一个请求的 IP 地址地理位置显示在“美国纽约”,而你的服务器也在“纽约”。理论上,同城的网络延迟应该在
5ms以内。 - 发现异常:如果该请求的
JA4L_a显示延迟高达150ms,这在物理上是不可能的(光速不允许)。这立刻暴露了该用户实际上位于地球的另一端(比如亚洲或东欧),只是通过纽约的 VPN 节点在访问。 识破 VPN 和 代理 (Proxy)
发现会话劫持 (自己挂VPN 会误判)
识别伪造的操作系统 (手机开热点会误判)
- 核心概念:JA4H 专门用于为 HTTP 客户端(浏览器、爬虫、恶意软件、API 客户端)生成指纹。
工作原理:
- 生成逻辑:提取请求中所有 Cookie 的名称和具体的值,计算 SHA256 哈希并截取前 12 个字符。
- 作用:因为包含了具体的 Cookie 值(如 Session ID),这一部分对每个用户/每个会话都是唯一的。安全团队可以使用
JA4H_d来追踪特定用户的行为轨迹,同时又不需要在日志中明文记录敏感的 Cookie 值,从而符合 GDPR 等隐私合规要求。 - 生成逻辑:提取请求中所有 Cookie 的名称/键(不包含具体的值),计算 SHA256 哈希并截取前 12 个字符。
- 作用:同一个网站或应用(例如某个特定的 OA 系统、Okta 登录页)通常会要求客户端携带固定名称的 Cookie。因此,访问同一网站的所有用户,其
JA4H_c通常是相同的。如果发现某个请求的JA4H_c与正常业务不符,可能意味着伪造请求。 - 生成逻辑:提取请求中所有 HTTP Header 的名称(排除掉
Cookie和Referer),按照它们在请求中出现的原始顺序排列,计算 SHA256 哈希值,并截取前 12 个字符。 - 作用:不同的浏览器、编程语言库(如 Python
requests、Gonet/http)或恶意软件在发送 HTTP 请求时,默认的请求头顺序和组合是不同的。这一部分可以精准识别底层的 HTTP 客户端工具。 chrome,request,mac,linux,android,scrapy,go,java headers 顺序对比
JA4H_b(974ebe531c03)JA4H_c(b66fa821d02c)JA4H_d(e97928733c74)JA4H_a(ge20cr13enus)- 与 JA4 类似,JA4H 也采用了模块化的格式,通常表现为
a_b_c_d的结构(有时简写为a_b或a_b_c,取决于具体实现和提取深度)。这种设计既方便人类阅读,又方便机器进行哈希匹配。 - 以一个真实的 JA4H 指纹为例:示例指纹(以 Google Chrome 浏览器为例)
- 核心概念:JA4 就是 JA3 的全面升级版和继任者,在不解密网络流量的情况下,通过观察客户端“打招呼”的方式,来精准识别这个客户端到底是 Chrome 浏览器、Python 爬虫、还是恶意软件(如木马、勒索软件)的技术
工作原理:
明文元数据:
密码套件哈希(排序 + 截断+过滤)
扩展与签名算法哈希(排序 + 截断+过滤)、
t= TCP (如果是 QUIC 则是 q)13= TLS 1.3d= 包含域名 SNI (如果是 IP 则是 i)15= 提供了 15 个密码套件16= 提供了 16 个 TLS 扩展h2= ALPN 协商为 HTTP/2- 以 JA4(TLS 客户端指纹) 为例,Part A 的格式为:
[协议][TLS版本][SNI][密码套件数量][扩展数量][ALPN] 示例:
t13d1516h2- 提取 TLS ClientHello 中的密码套件(Cipher Suites)列表。
- 核心动作: 先将这些套件按十六进制值进行排序,然后再计算 SHA256 哈希,最后只截取前 12 个字符,期间会忽视过滤GREASE 值。
- 原理优势: 排序保证了即使客户端打乱了密码套件的发送顺序,最终生成的哈希也是一致的。截取 12 位是为了保持指纹的简短。
- 提取 TLS 扩展(Extensions)和签名算法(Signature Algorithms)。
- 核心动作: 同样先进行排序,计算 SHA256 哈希,截取前 12 个字符。
- 原理优势: 这一步完美破解了现代浏览器的“TLS 扩展随机化”防御。因为无论浏览器怎么打乱扩展顺序,JA4 在计算前都会将其重新排序,从而保证同一个浏览器生成的 Part C 永远是稳定的。期间会忽视过滤GREASE 值。
- 最终生成的 JA4 指纹看起来像这样:
t13d1516h2_8daaf6152771_e5627efa2ab1 JA4 采用了创新的
a_b_c三段式结构JA4/JA4S (核心)
JA4H (核心)
JA4L/JA4LS (弱)
JA4X (无用)
JA4SSH (无用)
JA4T/JA4TS (弱)
JA4SCAN (弱)
Host User-Agent (默认是 python-requests/2.x.x) Accept-Encoding (通常是 gzip, deflate) Accept (通常是 */*) Connection (通常是 keep-alive)
Accept Accept-Language User-Agent Accept-Encoding Host Connection
Host Connection Accept-Encoding (通常是 gzip) User-Agent (通常是 okhttp/4.x.x 或自定义)
Host Accept Connection Accept-Language (苹果设备通常会带上系统语言) User-Agent (通常是 App名称/版本 CFNetwork/版本 Darwin/版本) Accept-Encoding
User-Agent (默认是 Java/1.8.0_xxx) Host Accept (通常是 text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2) Connection
Host (特殊处理,通常在最前) User-Agent (默认是 Go-http-client/1.1 或 2.0) Accept-Encoding (默认 gzip)(如果开发者添加了其他 Header,Go 通常会按字母顺序输出它们)
假设我们得到一个 JA4L 指纹示例:33_128_45
很多工具(如基于 Python 或 Go 编写的脚本)会伪造 HTTP 请求头中的 User-Agent,声称自己是 Windows 上的 Chrome 浏览器。
攻击者经常使用商业 VPN 或住宅代理来伪装自己的 IP 地址。
这一部分是 Cookie 键值对(Names + Values)的哈希值。
这一部分是 Cookie 键名(Fields)的哈希值。
这一部分是请求头(Headers)的哈希值。
Host Connection sec-ch-ua (客户端提示,Chrome 特有) sec-ch-ua-mobile sec-ch-ua-platform Upgrade-Insecure-Requests User-Agent Accept Sec-Fetch-Site (Fetch 元数据,防跨站攻击) Sec-Fetch-Mode Sec-Fetch-User Sec-Fetch-Dest Accept-Encoding Accept-Language
ge20cr13enus_974ebe531c03_b66fa821d02c_e97928733c74
这一部分是人类可读的,主要提取 HTTP 请求的高级特征。
Chrome 针对Ja3,Ja4+ 的策略
GREASE 机制 —— 终结 JA3 的“杀手”
- 对 JA3 的绝杀:JA3 是将所有加密套件和扩展按顺序拼接后进行 MD5 哈希。因为 Chrome 每次请求都会插入随机的 GREASE 值,导致同一个 Chrome 浏览器每次发出的 JA3 指纹都不一样。JA3 因此在现代浏览器面前彻底失效。
- JA4 的反击:JA4 之所以诞生,就是为了应对 GREASE。JA4 的算法在计算哈希之前,会强制识别并剔除所有已知的 GREASE 随机值,然后再进行排序,从而重新捕获 Chrome 的稳定指纹。
- Chrome 的做法:在发送 TLS Client Hello 时,Chrome 会故意且随机地插入一些完全无效的、伪造的加密套件(Cipher Suites)、扩展(Extensions)和 ALPN 协议。
- 目的:为了“训练”互联网上的防火墙和网关——“你们必须学会忽略不认识的扩展,不要一看到不认识的东西就拦截,否则以后我推出新协议你们全得瘫痪。”
对指纹的影响:
ECH (Encrypted Client Hello) —— 隐藏核心特征 (对服务器无用)
- JA4 的第一部分(
a)依赖于 SNI(域名)来判断特征(比如指纹里的d代表 Domain)。 - 一旦 ECH 全面普及,网络中间设备将无法看到真实的 SNI 和内部扩展,只能看到一个“外层”的伪装 Client Hello。这会极大削减 JA4 能够提取的特征维度,使得不同客户端的指纹趋于同质化(看起来都一样)。
- 当然服务端可以根据密钥解密生成ja4+
- Chrome 的做法:在传统的 TLS 握手中,Client Hello 是明文的,任何人(包括 JA4 探针)都能看到你访问的域名(SNI)和支持的扩展。ECH 技术使用服务器的公钥,将 Client Hello 中最敏感的部分(包括 SNI 域名和部分扩展)直接加密。
对指纹的影响:
引入后量子密码学 (PQC) —— 导致指纹频繁“突变”
- 每次 Chrome 灰度测试或正式上线一种新的后量子算法,它的 TLS 扩展列表和加密套件列表就会发生变化。
- 这导致安全厂商维护的 JA4 指纹库必须频繁更新。昨天还是这个指纹,今天 Chrome 自动升级后,指纹就变了。防守方如果把旧指纹写死在白名单里,就会导致大量正常用户被误杀。
- Chrome 的做法:为了防止未来的量子计算机破解现在的加密流量,Chrome 在 TLS 握手的
Key Share扩展中加入了极其庞大的后量子密钥数据,并引入了新的加密套件 ID。 对指纹的影响:
TLS 扩展顺序随机化 (TLS ClientHello Permutation)
- JA3 依赖绝对顺序,顺序一乱,哈希全变。
- JA4 的应对:JA4 极其聪明地采用了”强制字母表排序(Sorting)“机制。不管 Chrome 怎么打乱发送顺序,JA4 在计算哈希前都会自己重新按 a-z 排序。因此,Chrome 的顺序随机化对 JA4 无效。
- Chrome 的做法:传统的客户端(如 Python requests)发送 TLS 扩展的顺序是固定的(比如 A, B, C, D)。Chrome 可以随机打乱这些真实扩展的顺序(比如变成 C, A, D, B)。
对指纹的影响:
GREASE(Generate Random Extensions And Sustain Extensibility,RFC 8701)是 Chrome 在 TLS 握手阶段引入的最著名的机制。
以前叫 ESNI,现在升级为 ECH。这是 Chrome 正在大力推进的隐私保护技术。
Chrome 一直在引领密码学的升级。最近一两年,Chrome 开始默认启用后量子加密算法(如 X25519Kyber768Draft00,以及最新的 ML-KEM)。
除了插入随机的 GREASE 值,Chrome 底层的 BoringSSL 密码库还具备打乱扩展发送顺序的能力。
HTTP 层的策略:User-Agent 冻结与 Client Hints (针对 JA4H)
在 HTTP 层(影响 JA4H),Chrome 也在进行大刀阔斧的改革。
Chrome 的做法:
- 冻结 User-Agent:Chrome 认为传统的 UA 字符串包含了太多系统信息(如具体的操作系统版本、手机型号),容易被用来做浏览器指纹追踪。因此,Chrome 已经将 UA 字符串“冻结”(比如永远显示 Windows 10,即使你是 Windows 11)。
- 引入 Sec-CH-UA (Client Hints):Chrome 改用一系列
Sec-CH-UA-*请求头来按需传递设备信息。 对指纹的影响:
- 这极大地改变了 Chrome 的 JA4H 指纹(HTTP Header 顺序和名称)。
- 对于伪造请求的爬虫开发者来说,以前只需要伪造一个
User-Agent字符串就行了;现在,他们必须精确伪造十几个Sec-CH-UA相关的头部,且顺序必须与真实 Chrome 完全一致,否则就会被 JA4H 瞬间识破。
Http2指纹
用途:主要用于识别是否是浏览器,python, scrapy 为空, 不同浏览器值不一样。
HTTP/2指纹的核心组成
- SETTINGS帧参数
# 每个客户端发送的SETTINGS参数及其值 SETTINGS_HEADER_TABLE_SIZE: 4096 # 头部压缩表大小 SETTINGS_ENABLE_PUSH: 0/1 # 是否启用服务器推送 SETTINGS_MAX_CONCURRENT_STREAMS: 100 # 最大并发流数 SETTINGS_INITIAL_WINDOW_SIZE: 65536 # 初始窗口大小 SETTINGS_MAX_FRAME_SIZE: 16384 # 最大帧大小 SETTINGS_MAX_HEADER_LIST_SIZE: 262144 # 最大头部列表大小
- WINDOW_UPDATE行为
- 初始窗口大小
- 窗口更新策略
- 流控制模式
- 优先级(Priority)设置
Stream Dependencies (流依赖关系) ├── Weight (权重分配) ├── Exclusive Flag (独占标志) └── Dependency Tree (依赖树结构)
伪头部字段顺序
- 指纹生成原理
:method
:authority
:scheme
:path
不同浏览器之间的差异:
Google : m,a,s,p
Firefox: m,p,a,s
Curl: m,p,s,a
Python: m,a,s,p
// 示例:HTTP/2指纹生成过程
function generateHTTP2Fingerprint(connection) {
const fingerprint = {
// SETTINGS帧指纹
settings: {
headerTableSize: connection.settings.headerTableSize,
enablePush: connection.settings.enablePush,
maxConcurrentStreams: connection.settings.maxConcurrentStreams,
initialWindowSize: connection.settings.initialWindowSize,
maxFrameSize: connection.settings.maxFrameSize,
maxHeaderListSize: connection.settings.maxHeaderListSize
},
// 窗口更新模式
windowUpdate: connection.windowUpdatePattern,
// 优先级树
priorityStructure: connection.priorityTree,
// 伪头部顺序
pseudoHeaderOrder: connection.headerOrder
};
// 生成唯一指纹哈希
return hash(JSON.stringify(fingerprint));
}- 示例
1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p 1:65536 → SETTINGS_HEADER_TABLE_SIZE = 65536 (64KB) # HPACK头部压缩表的最大大小 2:0 → SETTINGS_ENABLE_PUSH = 0 (禁用) # 禁用服务器推送功能 4:6291456 → SETTINGS_INITIAL_WINDOW_SIZE = 6291456 (6MB) # 流级别的初始窗口大小(流控制) 6:262144 → SETTINGS_MAX_HEADER_LIST_SIZE = 262144 (256KB) # 最大头部列表大小 15663105 → 连接级别的窗口大小 # 约15.6MB的连接窗口 # 计算:15663105 ≈ 15.6MB 0 → 优先级/依赖信息 # 可能表示根流(Stream 0)或无特定依赖 m,a,s,p → 伪头部字段顺序 # m = :method (请求方法) # a = :authority (授权/主机) # s = :scheme (协议方案 http/https) # p = :path (路径)
相关引用
230K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2T1M7X3!0%4M7$3g2J5M7$3y4S2L8W2)9J5k6h3&6W2N6q4)9J5c8Y4A6Z5i4K6u0r3N6r3I4K6
a54K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6@1L8s2y4Q4x3X3g2T1M7X3!0%4M7$3g2J5L8r3g2S2K9%4y4Q4x3X3g2U0L8$3#2Q4x3V1k6B7M7$3!0F1
d6dK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2U0L8X3u0D9L8$3N6K6i4K6u0W2j5$3!0E0i4K6u0r3P5i4g2V1L8$3&6Y4k6r3!0F1k6#2)9J5c8Y4m8Q4x3V1j5I4y4U0j5#2y4o6j5K6y4W2)9J5k6h3S2@1L8h3H3`.
766K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6W2L8X3N6A6L8X3g2W2M7X3W2F1k6#2)9J5k6i4y4S2L8r3g2K6k6X3!0J5j5$3g2Q4x3X3g2U0L8$3#2Q4x3V1k6@1L8s2y4Q4x3X3c8X3K9h3&6Y4k6i4u0H3M7X3W2F1N6r3W2F1k6#2)9J5k6s2N6A6N6r3S2Q4x3X3c8B7j5e0y4Q4x3X3c8S2L8X3c8Q4x3X3c8B7j5e0y4K6i4K6u0V1x3U0b7%4x3K6j5J5z5o6f1#2z5e0j5%4i4K6u0r3 5c2K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6K6N6r3q4U0K9$3!0$3k6i4u0X3L8r3!0%4i4K6u0W2j5$3!0E0i4K6u0r3M7i4g2W2M7%4c8A6L8$3&6K6i4K6u0r3y4U0l9@1x3o6M7H3y4e0N6Q4x3V1k6H3P5i4c8Z5L8$3&6Q4x3X3c8J5k6i4q4#2k6i4y4@1M7#2)9J5k6r3u0W2K9h3&6Y4i4K6u0V1k6X3W2F1k6$3g2J5M7s2u0A6L8Y4c8W2k6l9`.`.