curl-impersonate: 主要ブラウザを偽装できる特別なcurlビルド
(github.com/lwthiker)- curl-impersonateは、Chrome、Edge、Safari、Firefoxのように見えるTLSおよびHTTPハンドシェイクを行うよう修正されたcurlビルドで、CLIツールおよび
libcurl代替ライブラリとして利用可能 - 一般的なHTTPクライアントやライブラリのClient HelloおよびHTTP/2設定は実際のブラウザと大きく異なるため、一部のWebサービスはTLS/HTTPハンドシェイクでクライアントを識別し、異なるコンテンツを提供する
- 実装はFirefox向けにNSS、Chrome系向けにBoringSSLを使用し、TLS拡張・SSLオプション・HTTP/2設定の変更、
--ciphers、--curves、ヘッダーなどの非デフォルトフラグの適用で構成される - 対応対象はChrome 99〜116、Android Chrome 99、Edge 99・101、Firefox 91 ESR〜117、Safari 15.3・15.5で、各対象ごとにwrapper scriptとtarget nameが提供される
- コマンドラインでは
curl_chrome116を実行し、ライブラリ統合ではcurl_easy_impersonate()またはLinuxのLD_PRELOADとCURL_IMPERSONATEで既存のlibcurl利用アプリに適用できる
curl-impersonateが解決する問題
- curl-impersonateは、curlを特別にビルドして、Chrome、Edge、Safari、Firefoxの4大ブラウザを偽装できるようにしたプロジェクト
- TLSとHTTPハンドシェイクを実際のブラウザと同一に実行できる
- 利用方法は2つある
- 通常のcurlに近いコマンドラインツール
- 既存の
libcurlの代わりに統合できるライブラリ
なぜ必要か
- HTTPクライアントがTLS対応Webサイトに接続すると、まずTLSハンドシェイクを行う
- TLSハンドシェイクの最初のメッセージはClient Helloであり、多くのHTTPクライアントやライブラリが生成するClient Helloは実際のブラウザと大きく異なる
- サーバーがHTTP/2を使用する場合、TLSハンドシェイクに加えてHTTP/2ハンドシェイクも行われ、そこでさまざまな設定が交換される
- 多くのHTTPクライアントやライブラリのHTTP/2設定も実際のブラウザと異なる
- 一部のWebサービスはこうした違いを利用して接続クライアントを識別し、クライアントごとに異なるコンテンツを提供する
- この方式はTLS fingerprintingおよびHTTP/2 fingerprintingと呼ばれる
- READMEでは、この方式の広範な利用によってWebがより開放的でなく、よりプライベートでなく、特定のWebクライアントにより限定されるものになったと述べている
仕組み
curlはブラウザのように見えるよう大幅にパッチが当てられている- 主な変更点は次のとおり
- Firefox版はOpenSSLの代わりに、Firefoxが使用するTLSライブラリであるNSSでcurlをコンパイルする
- Chrome版はGoogleのTLSライブラリであるBoringSSLでコンパイルする
- curlが各種TLS拡張やSSLオプションを設定する方法を変更する
- 新しいTLS拡張のサポートを追加する
- HTTP/2接続で使用する設定を変更する
--ciphers、--curves、一部の-Hヘッダーなど、非デフォルトフラグ付きでcurlを実行する
- その結果、ネットワークの観点ではcurlが実際のブラウザと同一に見える
- 技術的な詳細説明はpart a、part bにある
対応ブラウザと対象名
- 対応ブラウザ一覧は
browsers.jsonにも記載されている - Chrome系の対応対象
- Windows 10上のChrome 99、100、101、104、107、110、116
- Android 12上のChrome 99
- Windows 10上のEdge 99、101
- MacOS Big Sur上のSafari 15.3
- MacOS Monterey上のSafari 15.5
- Firefoxの対応対象
- Windows 10上のFirefox 91 ESR、95、98、100、102、109、117
- 各対応対象にはtarget nameとwrapper scriptがある
- 例:
chrome116targetはcurl_chrome116wrapper scriptで実行する - 例:
ff117targetはcurl_ff117wrapper scriptで実行する
- 例:
基本的な使い方
- 各対応ブラウザごとに、必要なヘッダーとフラグを含めて
curl-impersonateを実行するwrapper scriptがある - 実行例
curl_chrome116 https://www.wikipedia.org - コマンドラインフラグを追加するとcurlに渡される
- 一部のフラグはcurlのTLS signatureを変更し、検出される可能性がある
- wrapper scriptはデフォルトのHTTPヘッダーセットを使用する
- ヘッダーを変更したい場合は、目的に合わせてwrapper scriptを修正できる
- より多くのオプションは、
libcurl-impersonateをライブラリとして使う高度な利用方法に含まれる
インストールと配布方法
- 技術的な理由により、
curl-impersonateには2つのバージョンがある- chrome版: Chrome、Edge、Safariの偽装に使用
- firefox版: Firefoxの偽装に使用
- LinuxおよびmacOS Intel向けの事前コンパイル済みバイナリはGitHub releasesで提供される
- 事前コンパイル済みバイナリを使用する前に、NSSとCA証明書のインストールが必要
- Ubuntu:
sudo apt install libnss3 nss-plugin-pem ca-certificates - Red Hat/Fedora/CentOS:
yum install nss nss-pem ca-certificates - Archlinux:
pacman -S nss ca-certificates - macOS:
brew install nss ca-certificates
- Ubuntu:
- システムにはzlibも必要
- zlibはほぼ常に存在するが、一部の最小構成システムにはない場合がある
- 事前コンパイル済みLinuxバイナリはUbuntu向けにビルドされている
- 他のディストリビューションで証明書検証エラーが出る場合、CA証明書の場所をcurlに指定する必要があるかもしれない
curl_chrome116 https://www.wikipedia.org --cacert /etc/ssl/certs/ca-bundle.crt - ソースからのビルドはINSTALL.mdを参照
Dockerとパッケージ
- Alpine LinuxおよびDebianベースのDockerイメージがDocker Hubで提供される
- Dockerイメージにはバイナリとすべてのwrapper scriptが含まれる
- 例
# Firefox version, Alpine Linux docker pull lwthiker/curl-impersonate:0.6-ff docker run --rm lwthiker/curl-impersonate:0.6-ff curl_ff109 https://www.wikipedia.org # Chrome version, Alpine Linux docker pull lwthiker/curl-impersonate:0.6-chrome docker run --rm lwthiker/curl-impersonate:0.6-chrome curl_chrome110 https://www.wikipedia.org - ArchlinuxユーザーはAURパッケージを利用できる
- 事前コンパイル済みパッケージ: curl-impersonate-bin、libcurl-impersonate-bin
- ソースビルドパッケージ: curl-impersonate-chrome、curl-impersonate-firefox
- Mac向けの非公式Homebrew formulaはChrome専用で提供される
brew tap shakacode/brew brew install curl-impersonate
libcurl-impersonateの高度な使い方
libcurl-impersonate.soは、コマンドライン版curl-impersonateと同じ変更を加えてコンパイルされたlibcurl- 追加のAPI関数がある
CURLcode curl_easy_impersonate(struct Curl_easy *data, const char * target, int default_headers); chrome116のようなtarget nameで呼び出すと、wrapper scriptが設定していたオプションとヘッダーを内部で設定するdefault_headersが0の場合、組み込みのHTTPヘッダー一覧は設定しない- ユーザーが通常の
CURLOPT_HTTPHEADERlibcurlオプションで直接ヘッダーを指定する必要がある
- ユーザーが通常の
curl_easy_impersonate()は複数のlibcurlオプションを設定するCURLOPT_HTTP_VERSIONCURLOPT_SSLVERSION,CURLOPT_SSL_CIPHER_LIST,CURLOPT_SSL_EC_CURVES,CURLOPT_SSL_ENABLE_NPN,CURLOPT_SSL_ENABLE_ALPN- プロジェクト用の非標準オプションである
CURLOPT_HTTPBASEHEADER - プロジェクト用の非標準HTTP/2オプションである
CURLOPT_HTTP2_PSEUDO_HEADERS_ORDER,CURLOPT_HTTP2_NO_SERVER_PUSH - プロジェクト用の非標準TLSオプションである
CURLOPT_SSL_ENABLE_ALPS,CURLOPT_SSL_SIG_HASH_ALGS,CURLOPT_SSL_CERT_COMPRESSION,CURLOPT_SSL_ENABLE_TICKET - プロジェクト用の非標準TLSオプションである
CURLOPT_SSL_PERMUTE_EXTENSIONS
- その後で
curl_easy_setopt()を使って上記オプションのいずれかを呼ぶと、curl_easy_impersonate()が設定した値を上書きする
既存のlibcurlアプリへの適用
- アプリケーションがすでに
libcurlを使っている場合、LinuxではLD_PRELOADで既存ライブラリを実行時に差し替えられる CURL_IMPERSONATE環境変数を設定して自動偽装を適用するLD_PRELOAD=/path/to/libcurl-impersonate.so CURL_IMPERSONATE=chrome116 my_appCURL_IMPERSONATEには2つの効果があるcurl_easy_init()で新しいcurlハンドルが作られるたびにcurl_easy_impersonate()が自動で呼び出されるcurl_easy_reset()の後にもcurl_easy_impersonate()が自動で呼び出される
- HTTPヘッダーを細かく制御する必要がある場合は、
CURL_IMPERSONATE_HEADERS=noで組み込みヘッダー一覧を無効化し、自分で設定するLD_PRELOAD=/path/to/libcurl-impersonate.so CURL_IMPERSONATE=chrome116 CURL_IMPERSONATE_HEADERS=no my_app LD_PRELOAD方式はcurl自体には動作しない- curlツールがTLS設定を上書きするため
- curlにはwrapper scriptを使う必要がある
リポジトリ構成と貢献
- リポジトリには2つの主要フォルダがある
- Firefoxディレクトリの例には次のファイルが含まれる
- Dockerfile: すべての依存関係込みで
curl-impersonateをビルドするために使用 - curl_ff91esr、curl_ff95、curl_ff98: 正しいフラグで実行するwrapper script
- curl-impersonate.patch: curlがFirefoxと同じTLS拡張を使うようにする主要パッチで、libnghttp2とlibnssで静的コンパイルされるようにする
- Dockerfile: すべての依存関係込みで
- tests/signaturesは、偽装可能な既知ブラウザsignatureのYAMLデータベース
- 実際のcurlパッチは、upstream curlからforkされた別リポジトリで管理されている
- Firefox向け変更はimpersonate-firefoxブランチにある
- Chrome向け変更はimpersonate-chromeブランチにある
1件のコメント
Hacker News の意見
オリジナルより改善点がかなり多く、活発にメンテナンスされているフォークがある: https://github.com/lexiforest/curl-impersonate
Python ユーザー向けのバインディングもある: https://github.com/lexiforest/curl_cffi
皮肉なことに、そのリクエストは認証済みブラウザよりサーバー負荷が低いはずなのに、90年代に警告されていたディストピアとはこういうものなのかと思う。善良なオープンソース/ハッカーコミュニティの貢献者という立場を取りながら、この状況を作ったある会社の名前を、ここで誰が挙げられるのか気になる
今後 Ladybird が勢いを得てほしい。現在ネットワーク処理には正式な cURL を使っているが、この方法には難所があるかもしれない
例えば cURL にはまだいくつか制約があり、h2 上の WebSocket を扱えないと理解している。一方で成長中のブラウザエンジンが生まれれば、正規のトラフィックも標準 cURL と同じフィンガープリントを持つようになり、このフィンガープリンティング経路が消える可能性もある
実際のブラウザのワークフロー基準で、cURL にどんな機能が欠けているかを確認する良い試金石でもある
この数か月でフィンガープリンティングと実装定義の動作を「悪用」するレベルが大きく増えており、おそらく他のブラウザエンジンを潰そうとする試みに見える。既存の強者は競争をまったく好まない
相手側はこれを「AI ボットによる DDoS」として装おうとするが、そのうちどれだけが自分たちの作った問題なのか疑問だ
この curl は気に入っているが、何らかのコンポーネントが「追いつく」ためにごまかし役を担うと、メンテナンスしにくい「互換性」の重荷が積み上がるのではないかと心配している
理想的には、単に「こんにちは、私は curl です。入れてください」と言えるべきだ
もちろん問題はドレスコードにうるさいサーバー側にあり、その問題もまた変装して入ってくる詐欺師たちのせいで生じるので、鶏と卵のような循環になっている
[0] https://cybershow.uk/episodes.php?id=39
さらに、証明可能なほど安全ではない C++ で再設計されている点で、大きなセキュリティリスクがある
なりすまそうとしているプラットフォームに合わせて TTL 値をそろえるように
IP_TTLも設定しているのだろうか?そうでなければ、IP 層でもある程度フィンガープリンティングが可能だ。IP 層の TTL 値が 64 未満なら、現代の Windows 上で実行されていないか、デフォルト TTL を変更した現代の Windows だと明らかになる。現代の Windows のパケットのデフォルト TTL は 128 から始まり、他のほとんどのプラットフォームは 64 から始まるためだ
他のプラットフォームでもインターネット通信に問題はないので、現代の Windows から来た IP パケットは、リモート側では常に 64 以上、おそらく 64 より少し大きい TTL に見えるはずだ。ただし IP 層のフィンガープリンティングは難しいが、不可能ではない
https://en.wikipedia.org/wiki/TCP/IP_stack_fingerprinting
待って、TLS ハンドシェイクが違って見えるなら、Web ブラウザだと主張しているが実際には Python/PHP スクリプトであるトラフィックを nginx レベルでフィルタリングできるのでは?
例えば Chrome のユーザーエージェントを使っているが実際のブラウザではない場合だ。悪性ボットトラフィックの大半がこれに該当するだろうから、単にブロックできるならありがたい
仕組みは https://github.com/FoxIO-LLC/ja4/blob/main/technical_details... に詳しく書かれており、Nginx モジュールも提供されている: https://github.com/FoxIO-LLC/ja4-nginx-module
今まさに攻撃を受けているのでなければ、TLS フィンガープリントの許可リストを使うべきではない
素晴らしいツールだが、クライアントがブラウザかどうかは重要であるべきではない。現実にこういうツールが必要になるのは苦々しい
Chromeにユーザーエージェント拡張を追加してIEに変えて再試行したところ動作し、サイトのすべての機能も問題なかった。だから苦々しくもあり、腹立たしくもあった。おそらくその政府機関は25年前にWebサイトを作り、その後更新していないのだろう
こういうゲートキーピングも苦々しく感じる。私の理解では、自作のカスタムブラウザやユーザーエージェントは、彼らを説得できるだけの十分な影響力、資金、ユーザー数などが得られるまで、Cloudflare系のサイトでは決して動かないだろう
このツールは、レッドチーム作業で小さなbashスクリプトとGNU parallelを組み合わせて使うとなかなか良い
範囲指定されたアドレス帯の中で、さまざまな理由によりまともなブラウザにしか応答しなかったり、SNI設定が合っていないと応答しなかったりするHTTPSエンドポイントをマッピングする際に役立った。ヘッダー偽装用の
-Hのような通常のcurlオプションもそのまま使える当時のShow HN投稿: https://news.ycombinator.com/item?id=30378562
こういうものがここに上がるたび、いつも複雑な気持ちになる。一方では、人々の間にまだ多少の反骨心と独立性が残っていると示されるのは良いことだ
他方では、「自由は不安定」と見なされる他のプロジェクトと同じく、望まない注目を集めると、これに依存している人たちにとって状況がさらに悪くなる可能性もある。ブラウザを書くのは難しく、既存の強者たちはそれをさらに難しくし続けている
これを反骨心の問題とは見ていない。ソフトウェアがフィンガープリント可能になることは、プライバシー保護とソフトウェアの多様性を損なう
Webサイトがボットを問題ないと考えれば許可し、嫌ならユーザーエージェントをブロックしていた単純な時代が少し懐かしい
パッチ3つとシェルラッパーだけなら、Danielがコーディングに乗り出してもよさそうだ。個人的には、これは必ずメインラインcurlに入るべきだと思う