Cloudflare Turnstileソルバーライブラリ(ヘッドレスブラウザ不要)
Cloudflare Turnstile solver library (no headless browser required) (github.com)
要約
cfsolveは、ヘッドレスブラウザを使用せずにCloudflare Turnstileウィジェットやチャレンジページを解決できるRust製のライブラリおよびコマンドラインツールです。ウィジェットにはTurnstileトークンを、チャレンジページには`cf_clearance`クッキーを返します。ブラウザプロファイルを模倣し、SafariやChrome( Chromium)に対応しており、ビルドにはRust、CMake、C/C++コンパイラなどが必要です。
全文翻訳
cfsolve
cfsolveは、ヘッドレスブラウザなしでCloudflare Turnstileウィジェットやチャレンジページを解決するRust製のライブラリおよびコマンドラインツールです。ウィジェットにはTurnstileトークンを、チャレンジページにはcf_clearanceクッキーを返します。
Turnstileウィジェットは、ページのURL、サイトキー、そしてオプションでページのHTML(ほとんどの場合不要)を使用して解決できます。チャレンジページには、直接フェッチするか、ページのHTMLが必要です。
ブラウザプロファイル
cfsolveは、プロファイルファイルで記述された特定のブラウザを模倣します。profiles/safari-26-ios.json(iPhone上のSafari)が1つ含まれています。
各プロファイルは、ディレクトリ内のJSONとして保存されたチャレンジのブラウザチェックへの応答を参照します。コピーする際は、各プロファイルをそのディレクトリと一緒に保持してください。これらのファイルを作成するには、ある程度の(手動またはAIによる)労力が必要ですが、プロキシをcapture-proxyの実行アドレスに設定した実際のWebブラウザでチャレンジを解決することで、大幅に支援できます。現在、SafariとChrome(Chromium)プロファイルタイプのみがサポートされています。
ビルド
rust-toolchain.tomlでピン留めされたRustバージョン、CMake、C/C++コンパイラ、libclang、およびGit(BoringSSLをパッチしてビルドするため)が必要です。
cargo build --release --bin challenge
コマンドラインツール
./target/release/challenge --page 'https://example.com/login' /tmp/solve \
--solve --fingerprint profiles/safari-26-ios.json
成功すると、トークン(またはcf_clearance=...)が出力され、進捗状況が標準エラー出力に送信されます。ディレクトリ引数はデバッグ用のすべてのリクエストとレスポンスを保持するため、空であるか、まだ存在しない必要があります。
デフォルトでは、ページ上の最初のTurnstileウィジェットを解決するか、チャレンジページをクリアします。ページに複数のウィジェットがある場合に1つを選択するには、サイトキーを、サイトが設定している場合はアクションとcDataと共に渡します。--sitekeyを指定すると、チャレンジページでは失敗します。
./target/release/challenge --page 'https://example.com/login' /tmp/solve \
--solve --fingerprint profiles/safari-26-ios.json \
--sitekey 'SITEKEY' --action 'login' --cdata 'CUSTOM_DATA'
サイトキーのみがある場合は、--blank-pageを追加し、--pageをサイトの実際のURLに設定します。
| オプション | 説明 |
|---|---|
--fingerprint <file> | 使用するブラウザプロファイルファイル。必須。 |
--page <url> | ウィジェットのあるページ、またはチャレンジページ。 |
--solve | ウィジェットまたはチャレンジを解決します。これを指定しない場合、cfsolveはウィジェットの最初のペイロード送信後に停止します。 |
--sitekey <key> | サイトキーでウィジェットを選択します。 |
--action <value>, --cdata <value> | ウィジェットのアクションとcDataを上書きします。これには--sitekeyが必要です。 |
--blank-page | ページの読み込みをスキップします。ページURLの空白ページでウィジェットをレンダリングします。これには--solveと--sitekeyが必要です。 |
--proxy <url> | すべてのリクエストをhttp://、https://、socks5://、またはsocks5h://プロキシ経由で送信します。URLに認証情報を指定します(例: socks5h://user:password@host:1080)。 |
-h, --help | ヘルプを表示します。 |
ライブラリ
cfsolveをパス依存関係として追加し、その[patch]セクションをコピーします。CargoはトップレベルのCargo.tomlからのみパッチを読み取るためです。
[dependencies]
cfsolve = { path = "/path/to/cfsolve", default-features = false }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
[patch.crates-io]
btls-sys = { path = "/path/to/cfsolve/vendor/btls-sys" }
wreq = { path = "/path/to/cfsolve/vendor/wreq" }
http2 = { path = "/path/to/cfsolve/vendor/http2" }
default-features = falseは内部ツールをオフにし、ソルバーのみを残します。rust-toolchain.tomlのRustバージョンまたはそれ以降を使用してください。
use cfsolve::{Browser, Options, PageSource, Turnstile};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let browser = Browser::read("/path/to/cfsolve/profiles/safari-26-ios.json")?;
let options = Options::default();
let turnstile = Turnstile {
url: "https://example.com/login".parse()?,
page: PageSource::Navigate,
widget: None,
};
let token = browser.solve_turnstile(turnstile, &options).await?;
println!("{token}");
let cleared = browser.clear(&"https://example.com/".parse()?,
&options).await?;
println!("cf_clearance={}", cleared.clearance);
println!("{} cookies; protected page: {}", cleared.cookies.len(), cleared.page.url);
Ok(())
}
Browserを一度ロードして共有します。各並行操作は独自の接続とクッキーを取得します。
プロファイルと同じIPアドレスとユーザーエージェントからcf_clearanceクッキーを送信しないと、Cloudflareは拒否します。
サイトキーまたは独自のHTMLを持つTurnstile
PageSource::Navigateはページをフェッチします。URLとサイトキーのみの場合、PageSource::Blankを使用し、ウィジェットを指定します。
use cfsolve::{PageSource, Turnstile, Widget};
let turnstile = Turnstile {
url: "https://example.com/login".parse()?,
page: PageSource::Blank,
widget: Some(Widget {
sitekey: "SITEKEY".into(),
action: Some("login".into()),
cdata: None,
}),
};
let token = browser.solve_turnstile(turnstile, &options).await?;
サイトが使用する値、またはサイトが設定しない場合はNoneにactionとcdataを設定します。複数のウィジェットがある場合に1つを選択するため、またはサイトがJavaScriptで追加するウィジェットを指定するために、PageSource::Navigateと共にWidgetを渡すこともできます。
ログイン後のページなど、既にロードしたページがある場合は、そのURLとHTMLを渡します。
PageSource::Html {
html: std::fs::read_to_string("account.html")?,
referrer_policy: None, // またはレスポンスのReferrer-Policyヘッダー。
}
ウィジェットはクロスサイトiframeで実行されるため、ログインクッキーは必要ありません。Htmlの場合、widget: Noneはページ上の最初のウィジェットを解決し、Blankは常にWidgetを必要とし、サイトのURLの空白ページでレンダリングします。
チャレンジページ
browser.clear(&url, &options)はページをフェッチし、チャレンジをクリアします。返されるClearanceには、cf_clearanceクッキー、ページすべてのクッキー、および保護されたページが含まれます。チャレンジがない場合はError::NotChallengedで失敗します。
チャレンジページを自分でフェッチした場合は、browser.clear_fetched(page, &options)にレスポンスをFetchedPageとして渡します。プロファイルと同じIPアドレスとユーザーエージェントを使用してください。
URLがウィジェットまたはチャレンジページを表示するかどうかわからない場合は、browser.solveを使用します。これは両方のケースをカバーします。
use cfsolve::Outcome;
match browser.solve(&url, &options).await? {
Outcome::Token(token) => println!("{token}"),
Outcome::Cleared(cleared) => println!("cf_clearance={}", cleared.clearance),
}
オプション
Optionsを通じて、HTTP、HTTPS、またはSOCKS5を使用して、操作ごとにプロキシを設定できます。
let options = cfsolve::Options {
proxy: Some("socks5h://127.0.0.1:1080".into()),
..Default::default()
};
ライブラリ操作はデフォルトでサイレントです。進捗メッセージやデバッグファイルについては、Sinkを実装し、options.sinkをSome(Arc::new(your_sink))に設定してください。
エラー
| エラー | 原因 | リトライ? |
|---|---|---|
Error::Network | 接続失敗。 | はい |
Error::Rejected | Cloudflareが解決を拒否しました。 | はい |
Error::Blocked | サイトがチャレンジの代わりにエラーページを返しました。 | いいえ |
Error::Challenged | Turnstileトークンを要求しましたが、チャレンジページが表示されました。 | はい、クリア後 |
Error::NotChallenged | チャレンジクリアランスを要求しましたが、チャレンジがありませんでした。 | いいえ |
Error::Config | プロファイルまたはオプションが無効です。 | いいえ |
Error::Unsupported | その他の失敗(新しいチャレンジビルド、ウィジェットのないページなど)。 | いいえ |
ライセンス
MIT(vendor/内のパッチ適用済み依存関係は、それ自身のライセンスを保持します)。