nginxでJA4 fingerprintを出力するモジュールを書いた

はじめに

TLSのJA3フィンガープリントを出力するnginxのサードバーティーのモジュールをベースにJA4対応してみたというメモです。

とりあえず動くものができたっぽいという段階なので、本番運用に使えるレベルかは不明です。

JA3 / JA4について

JA3とJA4の概要についてはBot 対策の基礎技術 : JA3 / 4 Fingerprintingの記事がわかりやすかったです。ありがとうございます。

JA3のレポジトリはsalesforce/ja3: JA3 is a standard for creating SSL client fingerprints in an easy to produce and shareable way.です。2025-05-02にアーカイブされています。オリジナルの作者であるJohn AlthouseさんがFoxIO-LLCで最新のTLSフィンガープリンティングを保守されているとの記載がありました。

JA4のレポジトリはFoxIO-LLC/ja4: JA4+ is a suite of network fingerprinting standardsです。

JA4: TLS Client Fingerprintingの仕様はja4/technical_details/JA4.mdです。

JA4の他にJA4_rJA4_oJA4_roというフィンガープリントもあります。Raw Outputの項に説明があります。一致するかどうかを見るだけならJA4だけで良いですが、不一致な場合に原因を調査するには他の値も見ることになります。

JA3 / JA4はWiresharkでも確認できる

公式のFoxIO-LLC/ja4に含まれるCLIでtcpdumpでキャプチャーしたファイルを読んでJA4JA4_rJA4_oJA4_roの値を出力できます。

WiresharkでもClient Helloのパケットを選んで[Transport Layer Security]の下を展開していくとJA4JA4_rJA3 FullstringJA3の値を見られます。

JA3 / JA4のnginxモジュールについて

この組織に公式のnginxモジュールのレポジトリ FoxIO-LLC/ja4-nginx-module: Nginx module that calcuates fingerprints from the JA4+ suiteもありました。しかし、READMEに他のことを優先するため開発は中断していて、公開されているバージョンでは正しいJA4の値が生成されないと書いてありました。さらにLICENSEがFoxIO License 1.1という独自ライセンスだったので、こちらのレポジトリのコードは見ていません。

一方、FoxIO-LLC/ja4Licensingの項を見るとJA4: TLS Client Fingerprintingは3項BSDのオープンソースライセンスです。

JA4: TLS Client Fingerprinting is open-source, BSD 3-Clause, same as JA3. FoxIO does not have patent claims and is not planning to pursue patent coverage for JA4 TLS Client Fingerprinting. This allows any company or tool currently utilizing JA3 to immediately upgrade to JA4 without delay.

ただしそれ以外のJA4+(JA4S、JA4L、JA4LS、JA4H、JA4X、JA4SSH、JA4T、JA4TS、JA4TScanや今後追加される種別)はFoxIO License 1.1とのことです。

サードパーティのnginxのJA4のモジュールは検索したところなさそうでした。nginxのJA3のモジュールはサードパーティー製の2つが見つかりました。

fooinha/nginx-ssl-ja3: nginx module for SSL/TLS ja3 fingerprint.はREADMEにプロダクションレディではないと書いてあったので、phuslu/nginx-ssl-fingerprint: High performance ja3 and http2 fingerprint for nginx.のほうを試してみることにしました。

その後このモジュールを改変してJA3の改修とJA4の機能拡張を行いました。

作成したJA4のモジュール

https://github.com/hnakamur/nginx-ssl-fingerprint に置いています。

動的モジュールに対応

元のレポジトリはnginxの静的モジュールのみ対応だったので、動的モジュールにも対応しました。

OpenSSLのパッチを改修

JA3とJA4ではTLSのClient Helloに含まれる各種情報を収集してフィンガープリントの値を生成します。

そのうち、ClientHelloに含まれるTLS拡張タイプの一覧を取得する部分があります。

元のパッチでは patches/openssl.openssl-3.4.patch#L75-L103の部分で取得していました。

ちなみにapache/trafficserverにもJA3のプラグインとexperimentalなJA4のプラグインがあるのですが、そちらではSSL_client_hello_get1_extensions_presentというAPIを使用していました。

しかし、上記の方法ではChromeでアクセスしたときに公式CLIやWiresharkで確認したJA4JA3の値と一致しないことが判明しました。ハッシュ値化する前の元の値を見比べるとClient Helloに含まれる拡張タイプの一覧が一部不足していました。

そこでOpenSSLのtls_collect_extensions関数を元にopenssl.openssl-3.5.4.ja4.patchというパッチを作成しました。こちらはClient Helloに含まれる拡張をすべて取得します。

これでWiresharkのJA4JA3の値と一致するようになりました。

nginxのパッチも改修

元のパッチnginx-ssl-fingerprint/patches/nginx-1.27.patchnginx-1.29.3.ja4.patchのように改修しました。

struct ngx_ssl_connection_s内にJA3関連のフィールドを追加するのに加えて、JA4関連のフィールドも追加するようにしました。

ngx_ssl_handshake関数内でSSL_CTX_set_client_hello_cb関数を呼んでClient Helloを処理するときのコールバック関数を設定します(設定できる関数は1つだけで複数回呼ぶと上書きされてしまいます)。

元はngx_ssl_client_hello_ja3_cbという関数を設定していましたが、JA3に必要な情報に加えてJA4で必要な情報も取得するように変更し関数名もngx_ssl_client_hello_ja4_cbに変更しました。

また動的モジュール対応にしたので、モジュールを読み込んだ場合にのみSSL_CTX_set_client_hello_cb関数を呼ぶようにしました。これはモジュールを読み込まない場合になるべく不要な処理をしないようにという配慮です。

nginx-ssl-fingerprintモジュール本体も改修

JA4JA4_rJA4_oJA4_roの値を取得する変数を追加しました。変更内容はebfd72bです。

Ubuntu 24.04/22.04用のdebパッケージ

Ubuntu 24.04/22.04用のdebパッケージも作成しました。

パッチを当てたOpenSSLを共有ライブラリとしてビルドするのは管理が大変そうなので、nginxにスタティックリンクすることにしました。バージョンはDownloads | OpenSSL Libraryで最新のLTSである3.5.4です。