cfsolve:Cloudflare Turnstile 解決工具庫(無需 headless 瀏覽器)
Cloudflare Turnstile solver library (no headless browser required)
cfsolve 是一個用 Rust 編寫的庫與命令列工具,可在不使用 headless 瀏覽器的情況下解決 Cloudflare Turnstile 挑戰。
此工具可在無 headless 瀏覽器環境下解決 Cloudflare Turnstile,對自動化測試與網頁抓取提供便利。
cfsolve 是一個 Rust 函式庫與命令列工具,能在不使用無頭瀏覽器的情況下,解決 Cloudflare Turnstile 小工具與挑戰頁面。它會為小工具回傳 Turnstile 令牌,或為挑戰頁面回傳 cf_clearance cookie。
Turnstile 小工具可以使用頁面 URL、sitekey,並可選擇頁面 HTML(在大多數情況下不需要)進行解決。挑戰頁面則需要直接抓取或提供頁面 HTML。
瀏覽器配置檔
cfsolve 會模擬特定瀏覽器,透過配置檔描述。已包含一個:profiles/safari-26-ios.json(iPhone 上的 Safari)。
每個配置檔都引用已錄製的對挑戰瀏覽器檢查的回應,並以 JSON 存放於目錄中。複製時請保留配置檔及其目錄。產生這些檔案需要一定的手動(或 AI)工作,但可透過在真實網頁瀏覽器中解決挑戰,並將代理設定為正在執行的 capture-proxy 的位址,來大幅協助;此時會記錄傳送至 Cloudflare 的已解碼 payload。目前僅支援 Safari 與 Chrome(ium) 配置檔型別。
建置
你需要 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=...)並將進度寫入 stderr。目錄引數用於儲存所有請求與回應以供除錯,因此必須為空或尚未存在。
預設情況下,它會解決頁面上的第一個 Turnstile 小工具,或清除挑戰頁面。若頁面有多個小工具,請傳遞其 sitekey,並在站臺設定時提供 action 與 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'
若僅有 sitekey,請加上 --blank-page 並將 --page 設為站臺實際 URL。
| 選項 | 說明 |
|---|---|
--fingerprint <file> |
要使用的瀏覽器配置檔。必填。 |
--page <url> |
包含小工具或挑戰頁面的頁面。 |
--solve |
解決小工具或挑戰。若未提供,cfsolve 在傳送小工具的第一個 payload 後停止。 |
--sitekey <key> |
以 sitekey 選擇小工具。 |
--action <value>, --cdata <value> |
覆寫小工具的 action 與 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 並共用。並發操作各自獲得獨立連線與 cookie。
以與配置檔相同的 IP 位址與使用者代理傳送 cf_clearance cookie,否則 Cloudflare 會拒絕。
使用 sitekey 或自訂 HTML 的 Turnstile
PageSource::Navigate 抓取頁面。僅使用 URL 與 sitekey 時,請使用 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?;
將 action 與 cdata 設定為站臺使用的值,或在站臺未設定時使用 None。亦可傳遞 Widget 與 PageSource::Navigate 以從多個小工具中挑選一個,或提供站臺使用 JavaScript 加入的值。
對於已載入的頁面,例如登入後的頁面,請傳遞其 URL 與 HTML:
PageSource::Html { html: std::fs::read_to_string("account.html")?, referrer_policy: None, // Or the response's Referrer-Policy header. }
小工具執行於跨站 iframe 中,故不需登入 cookie。使用 Html、widget: None 可解決頁面上第一個小工具,而 Blank 必須提供 Widget 並於站臺 URL 的空白頁面上渲染。
挑戰頁面
browser.clear(&url, &options) 抓取頁面並清除挑戰。回傳的 Clearance 包含 cf_clearance cookie、頁面所有 cookie,以及受保護頁面。若無挑戰,將以 Error::NotChallenged 失敗。
如果您自己抓取了挑戰頁面,請將回應作為
FetchedPage 傳遞給 browser.clear_fetched(page, &options)。使用與個人資料相同的 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/ 中修補的相依套件保留其自身授權。