TLSフィンガープリンティングは、HTTPS接続を開くソフトウェアを、リクエスト内容が送信される前に、どのように開くか(どの暗号スイートをどの順序で提供するか、どの拡張機能や楕円曲線をリストするか)を見ることで識別します。 すべてのTLSライブラリとブラウザのバージョンは、サーバーに対してわずかに異なる方法で挨拶し、その挨拶は、その後のすべてが暗号化されているにもかかわらず、サーバーに平文で見えます。JA3とその後継であるJA4は、その挨拶を短いハッシュに変換します。アンチボットシステムは、そのハッシュを、主張されているブラウザが生成するはずのものと比較します。これが、PythonスクリプトがChromeを名乗り、完璧なレジデンシャルIPを介して接続しても、依然として検出される理由です。IPは「ベルリンの在宅ユーザー」と言い、TLSハンドシェイクは「スクリプト内のOpenSSL」と言うからです。
フィンガープリントの由来:ClientHello
HTTPS接続はTLSハンドシェイクから始まります。クライアントによって送信される最初のメッセージはClientHelloです。これは暗号化されていません。暗号化がまだネゴシエートされていないためです。これには、とりわけ次のものが含まれます。
- クライアントがサポートするTLS バージョン。
- 使用可能な暗号スイートのリスト(優先順位順)。
- 拡張機能のリスト(SNI(ホスト名)、ALPN(アプリケーションプロトコル、例:
h2、http/1.1)、サポートグループ(楕円曲線)、署名アルゴリズム、キー共有、セッションチケットなど)— これも特定の順序で。 - サポートする楕円曲線とポイント形式。
これらの値はどれも秘密ではなく、サイトごとに選択されるものではありません。これらはTLSライブラリとそれを使用するアプリケーションに組み込まれています。Chrome 128は特定のClientHelloを送信し、Firefoxは別のものを送信し、Pythonのsslモジュール(OpenSSL)は3番目のものを送信し、Goのcrypto/tlsは4番目のものを、curlは5番目のものを送信します。サーバーはすべての接続でClientHelloを表示します。応答するために必要だからです。それを記録してハッシュ化するコストはゼロです。
JA3とJA4:ハンドシェイクをシグネチャに変換する
JA3
2017年にSalesforceの研究者によって導入されたJA3は、ClientHelloの5つのフィールド(TLSバージョン、暗号スイート、拡張機能、楕円曲線、楕円曲線ポイント形式)をカンマ区切りの10進数リストとして連結し、その文字列をMD5ハッシュします。結果はcd08e31494f9531f560d64c695473da9のような32文字のフィンガープリントです。誰でもパケットキャプチャから計算でき、既知のクライアントにJA3ハッシュをマッピングするリスト(「このハッシュ = Windows上のChrome 120」、「このハッシュ = python-requests」)が広く共有されています。
JA3には、回避のための機能となった弱点があります。拡張機能リストを順序どおりにハッシュするため、Chrome(バージョン110以降)は接続ごとに拡張機能の順序をランダム化するため、単一のChromeインストールで多くの異なるJA3ハッシュが生成されます。これは、サーバーが1つのフィンガープリントに固執するのを防ぐために意図的に行われました。これにより、ブラウザの識別におけるJA3の有用性が低下しました。
JA4
2023年にFoxIOによってリリースされたJA4は、その問題を克服するように設計されました。暗号スイートと拡張機能のリストをハッシュする前にソートするため(Chromeのシャッフルはもはや問題になりません)、一部のフィールドは人間が読める形式を維持し、TCPまたはQUIC上の接続、SNIタイプ、ALPN値、TLSバージョン、暗号スイートと拡張機能の数など、より多くのシグナルを組み込みます。JA4フィンガープリントはt13d1516h2_8daaf6152771_e5627efa2ab1のようになります。最初のブロックは「TCP上のTLS 1.3、15の暗号スイート、16の拡張機能、ALPN h2」を示し、他の2つはソートされた暗号スイートリストとソートされた拡張機能リストと署名アルゴリズムの切り詰められたハッシュです。JA4は実際にはファミリーです(TLS用のJA4、HTTPヘッダー用のJA4H、サーバー側用のJA4S、証明書用のJA4Xなど)。今日、アンチボットベンダーが「TLSフィンガープリント」と言う場合、通常はJA4または同じ方法で構築された独自の同等物を意味します。
プロキシが役に立たない理由
プロキシはTLSハンドシェイクを中継しますが、書き換えはしません。HTTPプロキシを使用する場合、クライアントはCONNECT host:443を送信し、プロキシはTCPトンネルを開き、クライアントの独自のClientHelloがトンネルを通って宛先にそのまま届きます。SOCKS5では、同じことが1層下で発生します。したがって、宛先サーバーは2つの独立した事実を認識します。
- 送信元IP — プロキシのもので、レジデンシャルで問題ありません。
- ClientHello — クライアントのもので、どのライブラリがそれを構築したかを正確に示します。
アンチボットロジックは、単純な質問をします。TLSフィンガープリントはUser-Agentと一致しますか? User-Agentヘッダーは「Windows上のChrome 128」と主張しています。JA4は「Python経由のOpenSSL 3.x」を示しています。実際のChromeがそのJA4を生成することはありません。リクエストはフラグが立てられます。IPが悪いからではなく、ストーリーが内部的に一貫性がないからです。これは、「クリーンな」レジデンシャルプロキシが保護されたサイトでCAPTCHAを取得する最も一般的な理由であり、プロキシがどこからに答え、誰がには答えないと言う理由です。プロキシが何を隠し、何を隠さないかを参照してください。
プロキシが影響を与える可能性がある点が1つあります。一部のプロバイダーはTLSを自分たちで終了します(復号化して再暗号化する「man-in-the-middle」プロキシで、証明書のインストールが必要です)。その場合、宛先はプロキシのClientHelloを認識し、あなたのものではありません。これは通常、サーバー側ライブラリでありブラウザではないため、より悪い結果になります。通常のプロキシはこれを行いません。
各クライアントの外観
| クライアント | TLSフィンガープリントが示すもの | ブラウザのUser-Agentと一致しますか? |
|---|---|---|
| 実際のChrome / Firefox / Safari | そのブラウザとバージョン、そのOS上 | はい — それはブラウザです |
Python requests、httpx、aiohttp | Pythonのsslモジュールによって設定されたOpenSSL | いいえ — ヘッダーに関係なくスクリプトとして認識可能 |
Node.js fetch / axios | NodeのOpenSSL/BoringSSLビルド | いいえ |
Go net/http | Go独自のTLSスタック。特徴的です。 | いいえ |
| curl(デフォルトビルド) | ビルドされたOpenSSL/LibreSSL | いいえ |
| Puppeteer/Playwrightを介したヘッドレスChrome | 実際のChrome — 正しいTLS | TLSにははい。他のシグナル(自動化フラグ、プラグインの欠如)が漏れる可能性があります。 |
| 「偽装」クライアント(curl-impersonate、curl_cffi、tls-client) | 選択したブラウザのClientHelloをバイト単位で再現 | はい。ヘッダーも同じブラウザに一致させた場合。 |
一貫性のあるフィンガープリントを持つ方法
コストの増加順に、3つの正直な方法があります。
- 実際のブラウザを使用する。 PlaywrightまたはPuppeteerでChromeまたはFirefoxを自動化します。TLSは実際にブラウザのものになります。生のHTTPよりも遅くて重いですが、保護されたサイトではこれがベースラインです。各ブラウザコンテキストを独自のプロキシとペアにします。Playwright、Puppeteer、Seleniumでのプロキシを参照してください。
- 偽装HTTPクライアントを使用する。 curl_cffi(Python、curl-impersonateに基づく)やtls-clientなどのライブラリは、特定のブラウザのClientHelloとHTTP/2設定を再現するため、スクレイパーの速度とブラウザのフィンガープリントを実現できます。その場合、すべての他のヘッダー(User-Agent、
Accept-Language、sec-ch-uaクライアントヒント)を同じブラウザバージョンと一致させる必要があります。そうしないと、ある矛盾を別の矛盾に置き換えることになります。当社のPythonプロキシガイドは、requestsからcurl_cffiへの切り替えを示しています。 - 偽装をやめる。 ターゲットが公式APIを提供している場合は、実際のクライアントIDでそれを使用します。TLSフィンガープリンティングは、フィンガープリントが主張と矛盾する場合にのみ問題になります。
効果がないもの:requestsにChromeのUser-Agentを設定すること。1つのヘッダーを変更し、ハンドシェイクはそのままにします。不一致は以前よりも顕著になります。なぜなら、OpenSSLフィンガープリントを持つプレーンなpython-requests UAは、少なくとも自己整合的だからです。
全体像:フィンガープリントは積み重なる
TLSは1つのレイヤーです。最新のアンチボットベンダーは、HTTP/2接続(SETTINGSフレーム値、ウィンドウサイズ、ヘッダー順序と疑似ヘッダー順序 — アカマイフィンガープリンティングと呼ばれることもあります)、TCP/IPスタック(ウィンドウサイズ、TTL、オプション、古典的なp0f)、そしてもちろんブラウザ自体をJavaScriptを通じてフィンガープリントします。HTTPクライアントはJavaScriptを実行できません。各レイヤーは、他のレイヤーやIPと同じストーリーを伝える必要があります。ブラウザのTLSフィンガープリントとブラウザのHTTP/2設定を持つレジデンシャルIPでも、JavaScriptの実行がない場合は、JSチャレンジの実行を期待するサイトでは異常です。これが、「どのプロキシが検出を回避するか?」という質問に対する正直な答えが「単独ではどれもない」である理由であり、当社のランキングがプロバイダーがIP以外に何をするかを重視する理由です。エリートプロキシ比較を参照してください。
自分のフィンガープリントを確認する方法
いくつかの公開ページが、接続に対して計算したJA3/JA4と、それがどのブラウザに属すると思うかをエコーします。実際のブラウザから1つをロードし、同じプロキシを介してスクリプトでフェッチして、比較します。2つが異なる場合、スクリプトがブラウザとは異なる扱いを受ける理由が見つかります。プロキシを変更しても修正されません。ターゲットがエンタープライズアンチボットを実行していることがわかっている場合は、プロキシのテスト方法のチェックにこれを追加してください。
よくある質問
JA3とは何ですか?
TLS ClientHelloの5つのフィールド(バージョン、暗号スイート、拡張機能、曲線、ポイント形式)のMD5ハッシュで、クライアントソフトウェアを識別するために使用されます。スクリプトやライブラリには有効ですが、Chromeが拡張機能の順序をランダム化し始めてから、Chromeには信頼性が低くなりました。
JA4とは何ですか?
JA3の後継です。ハッシュする前にリストをソートするため、拡張機能のシャッフルは影響しません。フィンガープリントの一部を読み取り可能に保ち、トランスポート(TCP/QUIC)、ALPN、カウントを含みます。現在のTLSフィンガープリント検出のほとんどはこれに基づいています。
レジデンシャルプロキシはTLSフィンガープリントを変更しますか?
いいえ。プロキシはハンドシェイクをそのままトンネリングします。宛先はプロキシのIPとあなたのClientHelloを認識します。フィンガープリントを決定するのはクライアントソフトウェアだけです。
PythonでブラウザのTLSフィンガープリントを偽装できますか?
はい。curl_cffiなどの偽装クライアントを使用すると、選択したブラウザのClientHelloとHTTP/2の動作を再現できます。ヘッダーは同じブラウザと一致させる必要があります。プレーンなrequests/httpxではできません。
TLSフィンガープリンティングはブラウザフィンガープリンティングと同じですか?
いいえ。TLSフィンガープリンティングは暗号化された接続のハンドシェイクを読み取り、クライアントソフトウェアを識別します。ブラウザフィンガープリンティングはJavaScriptを実行し、デバイス(キャンバス、フォント、GPU)を識別します。アンチボットシステムは両方とIPレピュテーションを使用し、それらが一致するかどうかを確認します。
同じスクリプトが一方のサイトでブロックされ、もう一方ではブロックされないのはなぜですか?
TLSフィンガープリントチェックはエンタープライズアンチボット製品の機能であり、Web全般の機能ではないためです。そのような保護がないサイトはハンドシェイクを調べません。あるサイトはすべてのリクエストでUser-Agentと比較します。