Cloudflare Turnstile 解算器库(无需无头浏览器)
摘要
cfsolve 是一个 Rust 库和命令行工具,可在无需无头浏览器的情况下解算 Cloudflare Turnstile 小部件和挑战页面,通过浏览器指纹模拟返回 Turnstile 令牌或 cf_clearance cookie。
查看缓存全文
缓存时间: 2026/10/31 00:15
chris124567/cfsolve
来源:https://github.com/chris124567/cfsolve
cfsolve
cfsolve 是一个 Rust 库和命令行工具,无需无头浏览器即可解决 Cloudflare Turnstile 小部件和挑战页面。它为小部件返回 Turnstile 令牌,为挑战页面返回 cf_clearance Cookie。
Turnstile 小部件可以通过页面 URL、sitekey 以及可选的页面 HTML 来解决(在大多数情况下不需要提供 HTML)。挑战页面则需要直接抓取或提供页面的 HTML。
浏览器配置
cfsolve 模拟一个特定的浏览器,由配置文件描述。已内置一个:profiles/safari-26-ios.json(iPhone 上的 Safari)。每个配置文件引用一组已录制的挑战浏览器检查响应,以 JSON 格式存储在对应目录中。复制时请将配置文件与其目录一起复制。
生成这些文件需要一些手动(或 AI)工作,但可以通过在真实浏览器中设置代理为运行中的 capture-proxy 的地址来解决挑战,从而大幅简化流程,该代理会记录发送给 Cloudflare 的解码后的载荷。
目前仅支持 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 | 要使用的浏览器配置文件。必填。 |
--page | 包含小部件的页面,或挑战页面。 |
--solve | 解决小部件或挑战。不加此参数时,cfsolve 在发送小部件的第一个载荷后即停止。 |
--sitekey | 按 sitekey 选择小部件。 |
--action、--cdata | 覆盖小部件的 action 和 cData。需要配合 --sitekey。 |
--blank-page | 跳过页面加载,在页面 URL 处于空白页面上渲染小部件。需要配合 --solve 和 --sitekey。 |
--proxy | 通过 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 地址和 User-Agent 发送 cf_clearance Cookie,否则 Cloudflare 会拒绝它。
带 sitekey 的 Turnstile 或自定义 HTML
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。你也可以在 PageSource::Navigate 时传入 Widget,从多个小部件中选择一个,或提供站点通过 JavaScript 动态添加的小部件。
对于已经加载的页面(例如登录后的页面),传入其 URL 和 HTML:
PageSource::Html {
html: std::fs::read_to_string("account.html")?,
referrer_policy: None, // 或响应的 Referrer-Policy 头。
}
小部件运行在跨站 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 和 User-Agent。
如果你不确定某个 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 trait 并将 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/ 中的已修补依赖保留各自的许可证。
相似文章
Cloudflare Turnstile 现在要求可指纹识别的 WebGL
Cloudflare 的 Turnstile 现在要求使用 WebGL 指纹识别进行设备验证,从而阻止了 WebKitGTK 浏览器并引发隐私担忧。
@vintcessun: 用Claude订阅就能跑的8阶段漏洞发现代理,本质上就是Cloudflare Project Glasswing论文的复现。多窄代理+故意分歧验证+可达性门控,把“哪段代码真能被攻击者利用”拆成8个精确步骤。Recon用Opus拆任务,H…
介绍一个基于Claude订阅运行的8阶段漏洞发现代理,复现了Cloudflare Project Glasswing论文的核心思路,通过多窄代理、故意分歧验证和可达性门控将漏洞利用分析拆解为精确步骤,无需API密钥。
byJoey/cfnew
CFnew 是一个基于 Cloudflare Workers/Pages 的代理工具,支持 VLESS、Trojan、xhttp 多协议,内置延迟测试、订阅转换、图形化管理面板等功能。v2.9.8 更新实现了内部订阅转换,不再依赖外部服务,并优化了传输性能。
DeCloudflare
DeCloudflare 是一个托管在 GitLab 上的项目,提供绕过或与 Cloudflare 服务交互的工具。
culpert — 具备 span 归属能力和回归检测工具的 Rust 堆分配分析器
culpert 是一个实验性的 Rust 堆分配分析器,能将采样到的分配归属到 span,导出 pprof 兼容报告并在 CI 中检测分配回归,由 Cloudflare 开发并已在生产前捕获多个内存泄漏问题。