跳到正文
Hacker News (official RSS)(github.com)·· 2 天前精選品質評分71

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/ 中修補的相依套件保留其自身授權。