【编程技术-TLS 特征】此文章归类为:编程技术。
前言
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, 来识别客户端应用程序。
原理
支持的曲线点格式
格式类型:
实际使用:
uncompressed客户端支持的椭圆曲线算法列表,用于ECDHE密钥交换
supported_groups扩展(0x000a)的内容# 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_ECDHE_RSA_WITH_AES_256_GCM_SHA384
含义拆解:
客户端支持的最高 TLS 版本(如 TLS 1.2, TLS 1.3)
TLSVersion(TLS版本):
Ciphers Suites(密码套件):
Extensions(扩展列表)
EllipticCurves(椭圆曲线)
EllipticCurvePointFormats(椭圆曲线点格式)
生成过程
提取出这 5 个字段后,JA3 会按照固定的格式将它们拼接成一个字符串,并计算哈希值:
, 隔开;字段内部的多个值之间用连字符 - 隔开。TLSVersion,Ciphers,Extensions,EllipticCurves,EllipticCurvePointFormats769,47-53-5-10-49161-49162-49171-49172-50-56-19-4,0-10-11,23-24-25,0cd08e31494f9531f560d64c695473da9爬虫检测&应对
ClientHello 握手包、决定密码套件和扩展列表的,是你操作系统上的 OpenSSL 库以及 ssl 模块的默认配置。它们向OpenSSL 传递一个固定写死的、按特定优先级排序的密码套件列表(Cipher Suites)和支持的椭圆曲线列表。GREASE 机制
ClientHello 的密码套件和扩展列表中随机插入无效值。这意味着,同一个 Chrome 浏览器,每次发起请求时,其底层的 JA3 原始字符串都在变化,从而导致生成的 JA3 MD5 指纹每次都不一样!0x?A?A, eg:0x1A1A 等结尾的值)。ClientHello 消息)时,会故意且随机地将这些预留的“无效值”塞进密码套件列表或扩展列表中。操作:
结果:
爬虫检测
解决方式:
from curl_cffi import requests
rs = requests.get("6fdK9s2c8@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+
背景
原理
采用了 a_b_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[协议][TLS版本][SNI][密码套件数量][扩展数量][ALPN]示例:t13d1516h2
t13d1516h2_8daaf6152771_e5627efa2ab1所有的JA4+ 指纹都遵循a_b_c 的三段式格式:
匹配原理
JA4+ 家族矩阵
128,Linux/macOS/Android/iOS 默认是 64,而思科等网络设备通常是 255。42,由于它最接近 64,我们可以推断初始 TTL 是 64(Linux/Mac),并且该数据包经过了 64 - 42 = 22 个路由节点(Hops)。Chrome
requests 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 通常会按字母顺序输出它们)
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。工作原理:
核心应用场景:
8961,安全人员立刻就能知道它其实部署在 AWS(亚马逊云)上,因为这是 AWS 基础设施特有的 MSS 大小。它还能探测出目标是否隐藏在反向代理或端口转发之后。工作原理:当客户端(如浏览器、爬虫、扫描器)与服务器建立 TCP 连接时,会发送一个 SYN 包。JA4T 会提取这个包中的 4 个关键网络参数:
核心应用场景:
65535_2-4-8-1-3_1412_8 这样的指纹。User-Agent: Windows,JA4T 也能在 TCP 层看穿它其实是一台 Linux 机器。Masscan、ZMap 这样的黑客批量扫描工具,为了追求极速,通常会使用自己精简的 TCP/IP 栈,它们发出的 SYN 包极其简陋(例如没有 TCP 选项,没有 MSS)。防守方只需封禁这些特定的 JA4T 指纹,就能瞬间屏蔽互联网上 80% 以上的恶意扫描流量。工作原理:传统的证书哈希(如 SHA-256)是对整个证书内容进行计算,只要证书内容变了一个字母,哈希值就会完全改变。而 JA4X 的目的不是识别证书的具体内容,而是识别证书是如何生成的。它提取证书的三个核心结构部分:
a_b_c 格式的 JA4X 指纹。核心概念:JA4L 不看数据包里的内容,而是测量客户端与服务器之间的物理距离(基于光速和网络延迟)。
工作原理:
应用 (以下都可能会误判需要结合使用)
JA4L_a(纯网络延迟)和 JA4L_c(应用层延迟)。如果两者差异巨大,说明客户端在处理应用层数据时存在严重的性能瓶颈,或者流量经过了某些深度包检测(DPI)防火墙或复杂的代理网关。作用:
SYN-ACK 到收到客户端 ACK 的时间差。 假设我们得到一个 JA4L 指纹示例:33_128_45
JA4L_a (33) —— TCP 握手内的单向延迟
JA4L_b (128) —— 观察到的 TTL(Time to Live)
JA4L_c (45) —— 握手外的应用层延迟
很多工具(如基于 Python 或 Go 编写的脚本)会伪造 HTTP 请求头中的 User-Agent,声称自己是 Windows 上的 Chrome 浏览器。
User-Agent 说是 Windows,但 JA4L_b (TTL) 却是 50(推测初始值为 64,属于 Linux),这种底层网络特征与上层应用特征的矛盾,能立刻暴露出这是一个运行在 Linux 服务器上的伪造爬虫或黑客脚本。JA4H_d) 正常,操作系统是 Windows (JA4L_b 接近 128),延迟 (JA4L_a) 稳定在 20ms。JA4H_d) 发起了请求,但 JA4L_b 变成了 53(推测初始为 64,说明变成了 Linux 系统),且延迟 JA4L_a 变成了 200ms。攻击者经常使用商业 VPN 或住宅代理来伪装自己的 IP 地址。
5ms 以内。JA4L_a 显示延迟高达 150ms,这在物理上是不可能的(光速不允许)。这立刻暴露了该用户实际上位于地球的另一端(比如亚洲或东欧),只是通过纽约的 VPN 节点在访问。识破 VPN 和 代理 (Proxy)
发现会话劫持 (自己挂VPN 会误判)
识别伪造的操作系统 (手机开热点会误判)
工作原理:
这一部分是 Cookie 键值对(Names + Values)的哈希值。
JA4H_d 来追踪特定用户的行为轨迹,同时又不需要在日志中明文记录敏感的 Cookie 值,从而符合 GDPR 等隐私合规要求。这一部分是 Cookie 键名(Fields)的哈希值。
JA4H_c 通常是相同的。如果发现某个请求的 JA4H_c 与正常业务不符,可能意味着伪造请求。这一部分是请求头(Headers)的哈希值。
Cookie 和 Referer),按照它们在请求中出现的原始顺序排列,计算 SHA256 哈希值,并截取前 12 个字符。requests、Go net/http)或恶意软件在发送 HTTP 请求时,默认的请求头顺序和组合是不同的。这一部分可以精准识别底层的 HTTP 客户端工具。chrome,request,mac,linux,android,scrapy,go,java 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
JA4H_b (974ebe531c03)
JA4H_c (b66fa821d02c)
JA4H_d (e97928733c74)
JA4H_a (ge20cr13enus)a_b_c_d 的结构(有时简写为 a_b 或 a_b_c,取决于具体实现和提取深度)。这种设计既方便人类阅读,又方便机器进行哈希匹配。 ge20cr13enus_974ebe531c03_b66fa821d02c_e97928733c74
这一部分是人类可读的,主要提取 HTTP 请求的高级特征。
工作原理:
明文元数据:
密码套件哈希(排序 + 截断+过滤)
扩展与签名算法哈希(排序 + 截断+过滤)、
t = TCP (如果是 QUIC 则是 q)13 = TLS 1.3d = 包含域名 SNI (如果是 IP 则是 i)15 = 提供了 15 个密码套件16 = 提供了 16 个 TLS 扩展h2 = ALPN 协商为 HTTP/2[协议][TLS版本][SNI][密码套件数量][扩展数量][ALPN]示例:t13d1516h2
t13d1516h2_8daaf6152771_e5627efa2ab1JA4 采用了创新的 a_b_c 三段式结构
JA4/JA4S (核心)
JA4H (核心)
JA4L/JA4LS (弱)
JA4X (无用)
JA4SSH (无用)
JA4T/JA4TS (弱)
JA4SCAN (弱)
Chrome 针对Ja3,Ja4+ 的策略
GREASE 机制 —— 终结 JA3 的“杀手”
GREASE(Generate Random Extensions And Sustain Extensibility,RFC 8701)是 Chrome 在 TLS 握手阶段引入的最著名的机制。
对指纹的影响:
ECH (Encrypted Client Hello) —— 隐藏核心特征 (对服务器无用)
a)依赖于 SNI(域名)来判断特征(比如指纹里的 d 代表 Domain)。以前叫 ESNI,现在升级为 ECH。这是 Chrome 正在大力推进的隐私保护技术。
对指纹的影响:
引入后量子密码学 (PQC) —— 导致指纹频繁“突变”
Chrome 一直在引领密码学的升级。最近一两年,Chrome 开始默认启用后量子加密算法(如 X25519Kyber768Draft00,以及最新的 ML-KEM)。
Key Share 扩展中加入了极其庞大的后量子密钥数据,并引入了新的加密套件 ID。对指纹的影响:
TLS 扩展顺序随机化 (TLS ClientHello Permutation)
除了插入随机的 GREASE 值,Chrome 底层的 BoringSSL 密码库还具备打乱扩展发送顺序的能力。
对指纹的影响:
在 HTTP 层(影响 JA4H),Chrome 也在进行大刀阔斧的改革。
Chrome 的做法:
Sec-CH-UA-* 请求头来按需传递设备信息。对指纹的影响:
User-Agent 字符串就行了;现在,他们必须精确伪造十几个 Sec-CH-UA 相关的头部,且顺序必须与真实 Chrome 完全一致,否则就会被 JA4H 瞬间识破。Http2指纹
用途:主要用于识别是否是浏览器,python, scrapy 为空, 不同浏览器值不一样。
HTTP/2指纹的核心组成
# 每个客户端发送的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 # 最大头部列表大小
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 (路径)
相关引用
4a0K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2T1M7X3!0%4M7$3g2J5M7$3y4S2L8W2)9J5k6h3&6W2N6q4)9J5c8Y4A6Z5i4K6u0r3N6r3I4K6
39cK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6@1L8s2y4Q4x3X3g2T1M7X3!0%4M7$3g2J5L8r3g2S2K9%4y4Q4x3X3g2U0L8$3#2Q4x3V1k6B7M7$3!0F1
36aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2U0L8X3u0D9L8$3N6K6i4K6u0W2j5$3!0E0i4K6u0r3P5i4g2V1L8$3&6Y4k6r3!0F1k6#2)9J5c8Y4m8Q4x3V1j5I4y4U0j5#2y4o6j5K6y4W2)9J5k6h3S2@1L8h3H3`.
881K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6W2L8X3N6A6L8X3g2W2M7X3W2F1k6#2)9J5k6i4y4S2L8r3g2K6k6X3!0J5j5$3g2Q4x3X3g2U0L8$3#2Q4x3V1k6@1L8s2y4Q4x3X3c8X3K9h3&6Y4k6i4u0H3M7X3W2F1N6r3W2F1k6#2)9J5k6s2N6A6N6r3S2Q4x3X3c8B7j5e0y4Q4x3X3c8S2L8X3c8Q4x3X3c8B7j5e0y4K6i4K6u0V1x3U0b7%4x3K6j5J5z5o6f1#2z5e0j5%4i4K6u0r3 6fcK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6K6N6r3q4U0K9$3!0$3k6i4u0X3L8r3!0%4i4K6u0W2j5$3!0E0i4K6u0r3M7i4g2W2M7%4c8A6L8$3&6K6i4K6u0r3y4U0l9@1x3o6M7H3y4e0N6Q4x3V1k6H3P5i4c8Z5L8$3&6Q4x3X3c8J5k6i4q4#2k6i4y4@1M7#2)9J5k6r3u0W2K9h3&6Y4i4K6u0V1k6X3W2F1k6$3g2J5M7s2u0A6L8Y4c8W2k6l9`.`.
更多【编程技术-TLS 特征】相关视频教程:www.yxfzedu.com