JA3 是一种对传输层安全应用程序进行指纹识别的方法。它于 2017 年 6 月首次发布在 GitHub 上,是 Salesforce 研究人员 John Althouse、Jeff Atkinson 和 Josh Atkins 的作品。创建的 JA3 TLS/SSL 指纹可以在应用程序之间重叠,但仍然是一个很好的妥协指标 (IoC)。指纹识别是通过创建客户端问候消息的 5 个十进制字段的哈希来实现的,该消息在 TLS/SSL 会话的初始阶段发送
TLS 及其前身 SSL 用于为常见应用程序和恶意软件加密通信,以确保数据安全,因此可以隐藏在噪音中。要启动 TLS 会话,客户端将在 TCP 3 次握手之后发送 TLS 客户端 Hello 数据包。此数据包及其生成方式取决于构建客户端应用程序时使用的包和方法。服务器如果接受 TLS 连接,将使用基于服务器端库和配置以及 Client Hello 中的详细信息制定的 TLS Server Hello 数据包进行响应。由于 TLS 协商以明文形式传输,因此可以使用 TLS Client Hello 数据包中的详细信息来指纹和识别客户端应用程序
识别原理
计算ja3
客户端会在 TCP 3 次握手后发送 TLS 客户端的 Hello 数据包,而程(da)序(hei)员(ke)在写客户端的时候其实就已经确定了这个数据包里的一些特定内容会是什么样的,我们只需要将这些特定的内容提取出来,排好队,进行 hash,就是客户端的 TLS 指纹,即 ja3
,特定的内容就是ClientHello 的版本、可接受的加密算法、扩展列表中的每一个 type 值、支持的椭圆曲线和支持的椭圆曲线格式
1、JA3 不是简单地查看使用的证书,而是解析在 SSL 握手期间发送的 TLS 客户端 hello 数据包中设置的多个字段。然后可以使用生成的指纹来识别、记录、警报和/或阻止特定流量。
2、JA3 在 SSL 握手中查看客户端 hello 数据包以收集 SSL 版本和支持的密码列表。如果客户端支持,它还将使用所有支持的 SSL 扩展、所有支持的椭圆曲线,最后是椭圆曲线点格式。这些字段以逗号分隔,多个值用短划线分隔(例如,每个支持的密码将在它们之间用短划线列出)
3、JA3 方法用于收集 Client Hello 数据包中以下字段的字节的十进制值:版本、接受的密码列表、扩展列表、椭圆曲线和椭圆曲线格式。然后按顺序将这些值连接在一起,使用“,”分隔每个字段,使用“-”分隔每个字段中的每个值
ja3s
创建JA3之后,我们可以利用同样的方法对TLS握手的服务器端进行指纹识别,即对TLS Server Hello消息进行指纹识别。JA3S方法会收集Server Hello数据包中以下各个字段的十进制字节值:版本、可接受的加密算法和扩展列表。然后,它将这些值串联在一起,使用“,”来分隔各个字段,并通过“-”来分隔每个字段中的各个值
如何突破jas
有没有什么办法伪造 ja3?
其实是有的。以 Python 为例,requests 依赖是其实是对 urllib 的一个封装,https 底层还是依赖的 OpenSSL。我尝试找过 OpenSSL 有没有提供修改字段的方法,并没有发现。不过 cipher 的算法倒是可以直接修改,urllib3.util.ssl_.DEFAULT_CIPHERS = 'EECDH+AESGCMEDH+AESGCM'
即可。这样生成的 ja3 就不是 requests 默认的了,但只能骗过不是太高明的反爬机制。
同时,有人尝试过用 scapy 写一个代理,然后劫持 curl/requests 发出的请求,篡改 ClientHello 包里的相关字段,然后再发出,达到伪造的目的。一番尝试之后,我发现这是不可行的,OpenSSL 会校验 extension,如果和自己发出的不一致,则会报错:OpenSSL: error:141B30D9:SSL routines:tls_collect_extensions:unsolicited extension
。我的理解是这样的:ServerHello 的 Extension 是作为 ClientHello 的 Extension 的回应,前者应当是后者的子集。如果 Client 发现 ServerHello 中有一个扩展类型不存在与 ClientHello 中,那么它必须用一个 unsupported_extension alert 消息来丢弃此握手响应,所以如果准备用劫持的方式去伪造 ja3,这种依赖 OpenSSL 的库(比如 requests、curl)是不可行的,你只能祈祷 Server 别瞎返回,最好都不要响应任何 Extension。要么就去魔改 OpenSSL 以及 Python 调用 OpenSSL 的那个 .so,非常麻烦
最后有几个可以用来伪造 ja3 的库:
-
ja3transport
,golang 的库,这个的原理也是劫持 ClientHello 篡改,我认为不太靠谱。 -
curl-impersonate
,魔改的 curl,支持修改 ciphers 以及 curves。至于 extensions,我简单看了下作者的文章,他是通过使用与浏览器相同的 SSL 组件来模拟浏览器的 extensions,例如 Chrome 用的是BoringSSL
,FireFox 用的是NSS
,这个办法很聪明,但是它就没办法模拟任意 ja3 了。 -
CycleTLS
,有 golang 和 nodejs 的库,这个看代码是自己实现了 TLS 握手,实在是令人佩服。为了兼容 HTTP2 以及各种复杂的 TLS 参数,这个库目前还在艰难地维护当中, 不过只要不是特殊情况应该还是可以使用的。