TLS指纹-JA3

本文最后更新于:2021年12月19日 下午

前置知识:
Https-TLS相关

在线测试JA3: https://ja3er.com/json
github页:https://github.com/salesforce/ja3
老外的指导:https://github.com/yolossn/JA3-Fingerprint-Introduction

信息

JA3特征值其实就是TLSClient Hello 报文的摘要信息,对于同一台主机对服务器的访问而言,这个值会保持一致
类似的,JA3S的特征值是根据TLSServer Hello 报文生成的,对于同一台服务器对同一台主机的响应而言,这个值会保持一致

应用的策略相对简单
服务器可以利用 JA3值 用来标识客户端
客户端可以利用 JA3S值 用来标识服务器

利用同主机 JA3值 不变的特点来限制并发访问
利用同 服务器&主机 JA3S值不变的特点,防止渗透/欺骗

摘要流程

测试

访问 在线测试JA3,可以获取到当前电脑的ja3值

1
2
3
4
5
{
"ja3_hash":"b32309a26951912be7dba376398abc3b",
"ja3":"771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-21,29-23-24,0",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36"
}

这里会拿到两个值 JA3JA3_Hash

JA3 收集 Client Hello 报文 特征信息

第一次握手中,客户端会发送Client Hello 报文


JA3 会收集 Client Hello 报文的以下字段的十进制字节值

TLS版本 加密组件 扩展类型列表 支持组 椭圆曲线密码格式
Version CipherSuites Type List of Extensions Supported Group Elliptic Curve Formats

将这些值串联在一起。同一字段中的各值用-来分隔,不同字段用,来分隔
最终组合成 JA3 值:
771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-21,29-23-24,0
与在测试网页上获取的信息一致

JA3S MD5-Hash-32 收集 Server Hello 报文 信息

第二次握手中,客户端会发送Server Hello 报文
和第一步类似
JA3 会收集 Server Hello 报文 的以下字段的十进制字节值

TLS版本 加密组件 扩展类型列表
TLSVersion CipherSuites Extensions

算出类似的值,进行MD5计算即可得到结果

JA3记录

JA3官方统计了很多的JA3值,可以在 https://ja3er.com/getAllHashesJsonhttps://github.com/salesforce/ja3/blob/master/lists/osx-nix-ja3.csv 查看

解决方法

思考

既然是 Server Hello 报文 的摘要,那么改这个报文应该就能达到目的
理论上自己手工处理也不是问题

Golang 现成的解决办法

目前发现有 Golang 有现成的解决办法 ja3transport
代码参考

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package main

import (
"fmt"
"github.com/CUCyber/ja3transport"
tls "github.com/refraction-networking/utls"
"io/ioutil"
"net/http"
)


type Browser struct {
Ja3 string
UserAgent string
}


func req(browser Browser) {
config := tls.Config{
InsecureSkipVerify: true,

}
tr, _ := ja3transport.NewTransportWithConfig(browser.Ja3, &config)

client := &http.Client{
Transport: tr,
}
req, _ := http.NewRequest("GET", "https://ja3er.com/json", nil)
req.Header.Set("User-Agent", browser.UserAgent)
resp, err := client.Do(req)
if err != nil {
fmt.Println(err)
}

defer resp.Body.Close()
content, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(content))
}

func main() {
ja3List := []Browser{
{Ja3: "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53-10,0-23-65281-10-11-35-16-5-13-18-51-45-43-27,29-23-24,0",UserAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Safari/605.1.15"},
{Ja3: "771,4865-4866-4867-49196-49195-52393-49200-49199-52392-49188-49187-49162-49161-49192-49191-49172-49171-157-156-61-60-53-47-49160-49170-10,0-23-65281-10-11-16-5-13-18-51-45-43-21,29-23-24-25,0", UserAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 Edg/92.0.902.67"},
{Ja3: "771,4865-4866-4867-49196-49195-49188-49187-49162-49161-52393-49200-49199-49192-49191-49172-49171-52392-157-156-61-60-53-47-49160-49170-10,65281-0-23-13-5-18-16-11-51-45-43-10-21,29-23-24-25,0", UserAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 13_1_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Mobile/15E148 Safari/604.1"},
}
for _, ja3 := range ja3List{
req(ja3)
}
}