Le plan de cet article est le suivant. Veuillez suivre les idées de Lao Xu et créer progressivement votre propre empreinte digitale JA3.
Avant de commencer officiellement à comprendre ce qu'est l'empreinte digitale JA3, nous examinons d'abord le processus de prise de contact HTTPS, qui vous aidera à comprendre ce qui suit.
J'ai codé plus de 2 000 lignes de code juste pour clarifier le processus de prise de contact TLS. Cet article analyse principalement le processus d'authentification unidirectionnelle HTTPS et d'authentification bidirectionnelle (TLS1.3).
Dans Authentification unidirectionnelle, le client n'a pas besoin de certificat, il lui suffit de vérifier que le certificat du serveur est légal. Le processus de prise de contact et les messages échangés sont les suivants.
Dans authentification bidirectionnelle, le serveur et le client doivent vérifier la légitimité du certificat de l'autre partie. Le processus de prise de contact et les messages échangés sont les suivants.
Comparaison de l'authentification unidirectionnelle et de l'authentification bidirectionnelle :
Dans l'authentification unidirectionnelle et l'authentification bidirectionnelle, la totalité des données n'est envoyée et reçue que trois fois, et les données envoyées en une seule fois contiennent un ou plusieurs messages
clientHelloMsg
et serverHelloMsg
Non chiffré, tous les messages envoyés par la suite sont chiffrésclientHelloMsg
和serverHelloMsg
未经过加密,之后发送的消息均做了加密处理
Client和Server会各自计算两次密钥,计算时机分别是读取到对方的HelloMsg
和finishedMsg
之后
双向认证和单向认证相比,服务端多发送了certificateRequestMsgTLS13
消息
双向认证和单向认证相比,客户端多发送了certificateMsgTLS13
和certificateVerifyMsg
HelloMsg
et finishedMsg
Après 🎜🎜🎜🎜🎜🎜par rapport à l'authentification unidirectionnelle, le serveur envoie plus de certificateRequestMsgTLS13
message 🎜🎜🎜🎜🎜🎜Par rapport à l'authentification unidirectionnelle, le client envoie plus de certificateMsgTLS13
etcertificateVerifyMsgdeux messages 🎜Qu'il s'agisse d'une authentification unidirectionnelle ou d'une authentification bidirectionnelle, la compréhension par le serveur des informations de base du client repose entièrement sur le fait que le client l'informe activement au serveur, et les informations les plus critiques ne sont 客户端支持的TLS版本
、客户端支持的加密套件(cipherSuites)
、客户端支持的签名算法和客户端支持的密钥交换协议以及其对应的公钥
。这些信息均在包含clientHelloMsg
中,而这些信息也是生成JA3指纹的关键信息,并且clientHelloMsg
和serverHelloMsg
non cryptées. Non crypté signifie que la difficulté de modification est réduite, ce qui nous offre également la possibilité de personnaliser notre propre empreinte digitale JA3.
"Si vous souhaitez en savoir plus sur le processus de prise de contact HTTPS, veuillez lire l'article suivant :
Codez plus de 2 000 lignes de code juste pour que ce soit clair.
Codez plus de 2 000 lignes de code juste pour que ce soit clair. Processus de prise de contact TLS (suite)
”
J'ai déjà dit tant de choses, alors qu'est-ce que l'empreinte digitale JA3 exactement ? Selon l'article Open Sourcing JA3, Lao Xu l'a simplement compris comme JA3 est une méthode d'identification en ligne des empreintes digitales des clients TLS.
该方法用于收集clientHelloMsg
数据包中以下字段的十进制字节值:Version TLS
、Chiffres acceptés
、Liste des extensions
、Courbes elliptiques
et Formats de courbe elliptique
。然后,它将这些值串联起来,使用“,”来分隔各个字段,同时使用“ -”来分隔各个字段中的值。最后,计算这些字符串的md5哈希值,即得到易于使用和共享的长度为32老许将John Althouse文章中的抓包图结合Go源码中的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px; marge gauche : 2 px ; couleur d'arrière-plan : rgba (27, 31, 35, 0,05) ; famille de polices : « Operator Mono », Consolas, Monaco, Menlo, monospace ; coupure de mot : break-all ; couleur : rgb ( 255, 93, 108);">clientHelloMsg
结构体做了字段一一映射。clientHelloMsg
数据包中以下字段的十进制字节值:TLS Version
、Accepted Ciphers
、List of Extensions
、Elliptic Curves
和Elliptic Curve Formats
。然后,它将这些值串联起来,使用“,”来分隔各个字段,同时使用“-”来分隔各个字段中的值。最后,计算这些字符串的md5哈希值,即得到易于使用和共享的长度为32字符的指纹。
为了更近一步描述清楚这些数据的来源,老许将John Althouse
文章中的抓包图结合Go源码中的clientHelloMsg
https://github.com/Isites/go-coder/blob/master/http2/tls/handsh/msg_test.go
“md5虽然不安全,但是JA3选择md5作为哈希的主要原因是为了更好的向后兼容
”
很明显,JA3指纹也有其类似用途。举个简单的例子,攻击者构建了一个可执行文件,那么该文件的JA3指纹很有可能是唯一的。因此,我们能通过JA3指纹识别出一些恶意软件。
在本小节的最后,老许给大家推荐一个网站,该网站挂出了很多恶意JA3指纹列表。
https://sslbl.abuse.ch/ja3-fingerprints/
前文提到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 }
上述代码总结起来分为三步。
创建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)
最后得到的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的专属指纹呢?
通过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)
结果如下。
可以看到,最终在自定义JA3指纹后,http2的请求也能正常读取。至此,在支持http2的请求中构建专属的JA3指纹就完成了(生成JA3指纹的信息在clientHelloMsg
中,完成本部分仅是为了确保从发起请求到读取响应都能够正常进行)。
Quelques mots supplémentaires, compléter manuellement les requêtes http2 NewClientConn
a de grandes limites. Par exemple, vous devez gérer vous-même le cycle de vie de la connexion, vous ne pouvez pas vous reconnecter automatiquement, etc. Bien sûr, tout cela sera pour plus tard. S'il y a un réel besoin, les développeurs devront peut-être extraire le package net du code source go et le maintenir eux-mêmes.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!