ホームページ  >  記事  >  バックエンド開発  >  Go で独自の JA3 フィンガープリントを作成する

Go で独自の JA3 フィンガープリントを作成する

Go语言进阶学习
Go语言进阶学习転載
2023-07-24 16:20:53730ブラウズ
この記事では、https ハンドシェイク プロセスを簡単に説明し、JA3 フィンガープリントとは何か、および読者の質問に基づいて Go を使用して排他的な JA3 フィンガープリントをカスタマイズする方法について説明します。
Go で独自の JA3 フィンガープリントを作成する
#この記事の概要は次のとおりです。Lao Xu のアイデアに従って、徐々に独自の JA3 フィンガープリントを構築してください。

Go で独自の JA3 フィンガープリントを作成する

HTTPS ハンドシェイク プロセスを確認する

JA3 フィンガープリントとは何かを正式に理解し始める前に、次のことを確認しましょう。まず確認してください。HTTPS ハンドシェイク プロセスを見てみましょう。これは、以下のことを理解するのに役立ちます。

TLS ハンドシェイク プロセスを明確にするためだけに、2,000 行以上のコードをコーディングしました。この記事では主に HTTPS の一方向認証プロセスと双方向認証プロセス (TLS1.3) を分析します。

一方向認証では、クライアントは証明書を必要とせず、サーバー証明書が正当であることを確認することだけが必要です。ハンドシェイクのプロセスと交換されるメッセージは次のとおりです。

Go で独自の JA3 フィンガープリントを作成する
双方向認証

では、サーバーとクライアントの両方が相手の証明書の正当性を検証する必要があります。ハンドシェイクのプロセスと交換されるメッセージは次のとおりです。

Go で独自の JA3 フィンガープリントを作成する一方向認証と双方向認証の比較:
  1. 一方向認証と双方向認証では、合計データの送受信は 3 回のみであり、1 回に送信されるデータには 1 つ以上のメッセージが含まれます。

  2. clientHelloMsgserverHelloMsg は暗号化されません、後続のすべてのメッセージは暗号化されます

  3. クライアントとサーバーはそれぞれ 2 回キーを計算します 計算タイミングはそれぞれ相手の

    HelloMsgfinishedMsg を読み取った後です

  4. ##双方向認証と一方向認証と比較して、サーバーはより多くの
    certificateRequestMsgTLS13

    メッセージを送信します

  5. 双方向認証と一方向認証と比較すると、クライアントは
    certificateMsgTLS13

    certificateVerifyMsg という 2 つの追加メッセージを送信します。

一方向認証であろうと双方向認証であろうと、サーバーがクライアントの基本情報を理解できるかどうかは、クライアントがサーバーに積極的に伝えることに完全に依存しており、より重要な情報はクライアント サポート TLS バージョン , クライアントがサポートする暗号化スイート (cipherSuites) , クライアントがサポートする署名アルゴリズム、クライアントがサポートする鍵交換プロトコル、および対応する公開鍵 。この情報は clientHelloMsg に含まれており、JA3 フィンガープリントを生成するための鍵情報でもあり、 clientHelloMsgserverHelloMsg は暗号化されません。暗号化されていないということは、変更の難しさが軽減されることを意味し、独自の JA3 フィンガープリントをカスタマイズする可能性も提供します。

HTTPS ハンドシェイク プロセスの詳細について知りたい場合は、次の記事をお読みください。

2,000 行を超えるコードがコーディングされましたTLS を明確に説明するためだけにハンドシェイク プロセス

TLS ハンドシェイク プロセスを明確に説明するために 2,000 行を超えるコードがコーディングされています (続き)

#JA3 フィンガープリントとは##これまで何度も述べてきましたが、JA3 フィンガープリントとは正確には何でしょうか? Open Sourcing JA3 の記事によると、Lao Xu は JA3 が TLS クライアントのフィンガープリントをオンラインで識別する方法であると単純に理解しました。

このメソッドは、clientHelloMsg データ パケット内の次のフィールドの 10 進バイト値を収集するために使用されます: TLS バージョン 受け入れられた暗号数 拡張機能のリスト 楕円曲線、および 楕円曲線形式。次に、「,」を使用して各フィールドを区切り、「-」を使用して各フィールド内の値を区切って、これらの値を連結します。最後に、これらの文字列の md5 ハッシュが計算され、使いやすく共有しやすい 32 文字のフィンガープリントが生成されます。

これらのデータのソースをさらに詳しく説明するために、Lao Xu は、John Althouse 記事のパケット キャプチャ図と Go ソース コードの clientHelloMsg 構造を組み合わせました。 . フィールドは 1 つずつマッピングされます。

Go で独自の JA3 フィンガープリントを作成する

注意深い学生は、前の説明によれば、JA3 フィンガープリントには合計 5 つのデータ フィールドがあることに気づいたかもしれませんが、上の図では 4 つだけがマッピングされています。これは、TLS には多数の拡張フィールドがあるため、それらを 1 つずつ整理するつもりはありません。老徐はそれらを 1 つずつ列挙したわけではありませんが、単体テストを用意しており、詳細な研究に興味のある学生は、この単体テストを使用してデバッグ分析を行うことができます。

https://github.com/Isites/go-coder/blob/master/http2/tls/handsh/msg_test.go

JA3 フィンガープリントの使用法

前述の説明によると、JA3 フィンガープリントは md5 文字列です。日常の開発における md5 の使用を思い出してください。

  • 判断内容是否一致
  • 作为唯一标识

md5虽然不安全,但是JA3选择md5作为哈希的主要原因是为了更好的向后兼容

很明显,JA3指纹也有其类似用途。举个简单的例子,攻击者构建了一个可执行文件,那么该文件的JA3指纹很有可能是唯一的。因此,我们能通过JA3指纹识别出一些恶意软件。

在本小节的最后,老许给大家推荐一个网站,该网站挂出了很多恶意JA3指纹列表。

https://sslbl.abuse.ch/ja3-fingerprints/

构建专属的JA3指纹

http1.1的专属指纹

前文提到clientHelloMsgserverHelloMsg未经过加密,这为定制自己专属的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
}

上述代码总结起来分为三步。

  1. 创建TCP连接

  2. 构建clientHelloMsg需要的信息

  3. 完成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指纹如下。

Go で独自の JA3 フィンガープリントを作成する

我们已经得到了第一个JA3指纹,这个时候对代码稍加改动以期得到专属的JA3指纹。例如我们将2333这个数值加入到CipherSuites列表中,最后得到结果如下。

Go で独自の JA3 フィンガープリントを作成する

最终,JA3指纹又发生了变化,并且可称得上是自己专属的指纹。不用我说,看标题就应该知道问题还没有结束。从前面请求得到JA3指纹的结果图也可以看出来,当前使用的协议为http1.1,因此老许从某度中找了一个支持http2的链接继续验证。

Go で独自の JA3 フィンガープリントを作成する

看过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)

结果如下。

Go で独自の JA3 フィンガープリントを作成する

可以看到,最终在自定义JA3指纹后,http2的请求也能正常读取。至此,在支持http2的请求中构建专属的JA3指纹就完成了(生成JA3指纹的信息在clientHelloMsg中,完成本部分仅是为了确保从发起请求到读取响应都能够正常进行)。

少し補足すると、http2 リクエストを手動で完了する NewClientConn には大きな制限があります。たとえば、接続のライフサイクルを自分で管理する必要がある、自動的に再接続できないなどです。もちろん、これらはすべて後のことなので、これが本当に必要な場合は、開発者が go のソース コードからネット パッケージをフォークし、自分でメンテナンスする必要があるかもしれません。

以上がGo で独自の JA3 フィンガープリントを作成するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はGo语言进阶学习で複製されています。侵害がある場合は、admin@php.cn までご連絡ください。