Garis besar artikel ini adalah seperti berikut. Sila ikuti idea Lao Xu dan bina cap jari JA3 anda sendiri secara beransur-ansur.
Sebelum kami mula memahami secara rasmi apa itu cap jari JA3, kami semak dahulu proses jabat tangan HTTPS, yang akan membantu memahami perkara berikut.
Saya telah mengekodkan lebih daripada 2,000 baris kod hanya untuk menjelaskan proses jabat tangan TLS Artikel ini terutamanya menganalisis proses pengesahan sehala dan pengesahan dua hala HTTPS (TLS1.3).
Dalam Pengesahan sehala, pelanggan tidak memerlukan sijil, ia hanya perlu mengesahkan bahawa sijil pelayan itu sah. Proses berjabat tangan dan pertukaran msg adalah seperti berikut.
Dalam pengesahan dua hala, kedua-dua pelayan dan pelanggan perlu mengesahkan kesahihan sijil pihak yang satu lagi. Proses berjabat tangan dan pertukaran msg adalah seperti berikut.
Perbandingan pengesahan sehala dan pengesahan dua hala:
Dalam pengesahan sehala dan pengesahan dua hala, jumlah data dihantar dan diterima hanya tiga kali, dan data yang dihantar dalam satu masa mengandungi satu atau lebih mesej
clientHelloMsg
dan serverHelloMsg
Tidak disulitkan, semua mesej yang dihantar selepas itu disulitkanclientHelloMsg
和serverHelloMsg
未经过加密,之后发送的消息均做了加密处理
Client和Server会各自计算两次密钥,计算时机分别是读取到对方的HelloMsg
和finishedMsg
之后
双向认证和单向认证相比,服务端多发送了certificateRequestMsgTLS13
消息
双向认证和单向认证相比,客户端多发送了certificateMsgTLS13
和certificateVerifyMsg
HelloMsg
dan finishedMsg
Selepas 🎜🎜🎜🎜🎜🎜Berbanding dengan pengesahan sehala, pelayan menghantar lebih banyak certificateRequestMsgTLS13
mesej 🎜🎜🎜🎜🎜🎜Berbanding dengan pengesahan sehala, pelanggan menghantar lebih banyak certificateMsgTLS13
andcertificateVerifyMsgdua mesej 🎜Sama ada pengesahan sehala atau pengesahan dua hala, pemahaman Pelayan tentang maklumat asas Klien bergantung sepenuhnya pada Klien secara aktif memberitahu Pelayan, dan maklumat yang lebih kritikal adalah 客户端支持的TLS版本
、客户端支持的加密套件(cipherSuites)
、客户端支持的签名算法和客户端支持的密钥交换协议以及其对应的公钥
。这些信息均在包含clientHelloMsg
中,而这些信息也是生成JA3指纹的关键信息,并且clientHelloMsg
和serverHelloMsg
tidak disulitkan. Tidak disulitkan bermakna kesukaran pengubahsuaian dikurangkan, yang juga memberikan kemungkinan untuk kami menyesuaikan cap jari JA3 kami sendiri.
"Jika anda berminat untuk mengetahui butiran lanjut tentang proses jabat tangan HTTPS, sila baca artikel berikut:
Kod lebih daripada 2,000 baris kod hanya untuk menjelaskannya.
Kod lebih daripada 2,000 baris kod hanya untuk menjelaskannya. Menurut artikel Open Sourcing JA3, Lao Xu hanya memahaminya sebagai JA3 ialah kaedah pengenalan dalam talian cap jari pelanggan TLS.
该方法用于收集
clientHelloMsg
数据包中以下字段的十进制字节值:Versi TLS
、Sifir Diterima
、Senarai Sambungan
、Eliptik Curves
和Format Lengkung Eliptik
。然后,它将这些值串联起来,使用“,”来分隔吗中吗中吗中中文-”来分隔各个字段中的值。最后,计算这些字符串的md5哈希值,即得到易于使得到易于使得到易于使用和的3于使用和的3于使用和指纹。clientHelloMsg
数据包中以下字段的十进制字节值:TLS Version
、Accepted Ciphers
、List of Extensions
、Elliptic Curves
和Elliptic Curve Formats
。然后,它将这些值串联起来,使用“,”来分隔各个字段,同时使用“-”来分隔各个字段中的值。最后,计算这些字符串的md5哈希值,即得到易于使用和共享的长度为32字符的指纹。为了更近一步描述清楚这些数据的来源,老许将
为了更近一步描述清楚这些数据的来源,老许将John Althouse
文章中的抓包图结合Go源码中的clientHelloMsg
John Althouse文章中的抓包图结合Go源码中的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px; margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: " operator mono consolas monaco menlo monospace break-all rgb>clientHelloMsg
结构体做了字段一一映射。细心的同学南段一一映射。细心的同学可管前文描述JA3指纹总共有5个数据字段,而上图却只映射了4个。那是因为 TLS的extension字段比较多,老许就不一一整理了。虽然沗一一虽然没了准备了一个单元测试,有兴趣深入研究的同学可以通过这个单元测试进行调试分析。
🎜🎜JA3指纹用途🎜🎜🎜根据公是根据前一个md5字符串。请大家回想一下在平时的开发中md5的用途。🎜https://github.com/Isites/go-coder/blob/master/http2/tls/handsh/msg_test.goSalin selepas log masuk
判断内容是否一致 作为唯一标识 “md5虽然不安全,但是JA3选择md5作为哈希的主要原因是为了更好的向后兼容
”很明显,JA3指纹也有其类似用途。举个简单的例子,攻击者构建了一个可执行文件,那么该文件的JA3指纹很有可能是唯一的。因此,我们能通过JA3指纹识别出一些恶意软件。
在本小节的最后,老许给大家推荐一个网站,该网站挂出了很多恶意JA3指纹列表。
https://sslbl.abuse.ch/ja3-fingerprints/Salin selepas log masuk构建专属的JA3指纹
http1.1的专属指纹
前文提到
clientHelloMsg
和serverHelloMsg
未经过加密,这为定制自己专属的JA3指纹提供了可能,而在github上面有一个库(https://github.com/refraction-networking/utls)可以在一定程度上修改clientHelloMsg
。下面我们将通过这个库构建一个自己专属的JA3指纹。// 关键import import ( xtls "github.com/refraction-networking/utls" "crypto/tls" ) // 克隆一个Transport tr := http.DefaultTransport.(*http.Transport).Clone() // 自定义DialTLSContext函数,此函数会用于创建tcp连接和tls握手 tr.DialTLSContext = func(ctx context.Context, network, addr string) (net.Conn, error) { dialer := net.Dialer{} // 创建tcp连接 con, err := dialer.DialContext(ctx, network, addr) if err != nil { return nil, err } // 根据地址获取host信息 host, _, err := net.SplitHostPort(addr) if err != nil { return nil, err } // 构建tlsconf xtlsConf := &xtls.Config{ ServerName: host, Renegotiation: xtls.RenegotiateNever, } // 构建tls.UConn xtlsConn := xtls.UClient(con, xtlsConf, xtls.HelloCustom) clientHelloSpec := &xtls.ClientHelloSpec{ // hellomsg中的最大最小tls版本 TLSVersMax: tls.VersionTLS12, TLSVersMin: tls.VersionTLS10, // ja3指纹需要的CipherSuites CipherSuites: []uint16{ tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, // tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, // tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, }, CompressionMethods: []byte{ 0, }, // ja3指纹需要的Extensions Extensions: []xtls.TLSExtension{ &xtls.RenegotiationInfoExtension{Renegotiation: xtls.RenegotiateOnceAsClient}, &xtls.SNIExtension{ServerName: host}, &xtls.UtlsExtendedMasterSecretExtension{}, &xtls.SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []xtls.SignatureScheme{ xtls.ECDSAWithP256AndSHA256, xtls.PSSWithSHA256, xtls.PKCS1WithSHA256, xtls.ECDSAWithP384AndSHA384, xtls.ECDSAWithSHA1, xtls.PSSWithSHA384, xtls.PSSWithSHA384, xtls.PKCS1WithSHA384, xtls.PSSWithSHA512, xtls.PKCS1WithSHA512, xtls.PKCS1WithSHA1}}, &xtls.StatusRequestExtension{}, &xtls.NPNExtension{}, &xtls.SCTExtension{}, &xtls.ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}}, // ja3指纹需要的Elliptic Curve Formats &xtls.SupportedPointsExtension{SupportedPoints: []byte{1}}, // uncompressed // ja3指纹需要的Elliptic Curves &xtls.SupportedCurvesExtension{ Curves: []xtls.CurveID{ xtls.X25519, xtls.CurveP256, xtls.CurveP384, xtls.CurveP521, }, }, }, } // 定义hellomsg的加密套件等信息 err = xtlsConn.ApplyPreset(clientHelloSpec) if err != nil { return nil, err } // TLS握手 err = xtlsConn.Handshake() if err != nil { return nil, err } fmt.Println("当前请求使用协议:", xtlsConn.HandshakeState.ServerHello.AlpnProtocol) return xtlsConn, err }Salin selepas log masuk上述代码总结起来分为三步。
创建TCP连接
构建
clientHelloMsg
需要的信息完成TLS握手
有了上述代码后,我们通过请求
https://ja3er.com/json
来得到自己的JA3指纹。c := http.Client{ Transport: tr, } resp, err := c.Get("https://ja3er.com/json") if err != nil { fmt.Println(err) return } bts, err := ioutil.ReadAll(resp.Body) resp.Body.Close() fmt.Println(string(bts), err)Salin selepas log masuk最后得到的JA3指纹如下。
我们已经得到了第一个JA3指纹,这个时候对代码稍加改动以期得到
专属
的JA3指纹。例如我们将2333
这个数值加入到CipherSuites
列表中,最后得到结果如下。最终,JA3指纹又发生了变化,并且可称得上是自己专属的指纹。不用我说,看标题就应该知道问题还没有结束。从前面请求得到JA3指纹的结果图也可以看出来,当前使用的协议为
http1.1
,因此老许从某度中找了一个支持http2的链接继续验证。看过Go发起HTTP2.0请求流程析(前篇)这篇文章的同学应该知道,http2连接在建立时需要发送
PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
这么一个字符串。很明显,在自定义了DialTLSContext
函数之后相关流程缺失。此时,我们该如何构建http2的专属指纹呢?http2的专属指纹
通过
DialTLSContext
拨号之后只能得到一个已经完成TLS握手的连接,此时它还不支持http2的数据帧
、多路复用
等特性。所以,我们需要自己构建一个支持http2各种特性的连接。下面,我们通过
golang.org/x/net/http2
来完成自定义TLS握手流程后的http2请求。// 手动拨号,得到一个已经完成TLS握手后的连接 con, err := tr.DialTLSContext(context.Background(), "tcp", "dss0.bdstatic.com:443") if err != nil { fmt.Println("DialTLSContext", err) return } // 构建一个http2的连接 tr2 := http2.Transport{} // 这一步很关键,不可缺失 h2Con, err := tr2.NewClientConn(con) if err != nil { fmt.Println("NewClientConn", err) return } req, _ := http.NewRequest("GET", "https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/topnav/newzhidao-da1cf444b0.png", nil) // 向一个支持http2的链接发起请求并读取请求状态 resp2, err := h2Con.RoundTrip(req) if err != nil { fmt.Println("RoundTrip", err) return } io.CopyN(io.Discard, resp2.Body, 2<<10) resp2.Body.Close() fmt.Println("响应code: ", resp2.StatusCode)Salin selepas log masuk结果如下。
可以看到,最终在自定义JA3指纹后,http2的请求也能正常读取。至此,在支持http2的请求中构建专属的JA3指纹就完成了(生成JA3指纹的信息在
clientHelloMsg
中,完成本部分仅是为了确保从发起请求到读取响应都能够正常进行)。Beberapa perkataan tambahan, melengkapkan permintaan http2 secara manual
NewClientConn
mempunyai had yang besar. Contohnya, anda perlu mengurus sendiri kitaran hayat sambungan, tidak boleh menyambung semula secara automatik, dsb. Sudah tentu, ini semua perkara untuk kemudiannya Jika terdapat keperluan sebenar untuk ini, pembangun mungkin perlu mencabut pakej bersih daripada kod sumber go dan mengekalkannya sendiri.
Atas ialah kandungan terperinci Bina cap jari JA3 anda sendiri dengan Go. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!