| ## v6.2 β Full Chrome TLS Impersonation (BoringSSL) |
|
|
| ### Critical Fix: Complete Browser Fingerprint Impersonation |
|
|
| The v6.1 fixes (Chrome headers, HTTP/2, cookies) were necessary but **not sufficient**. |
| Cloudflare uses **three fingerprinting layers simultaneously**, and v6.1 only addressed one: |
|
|
| | Layer | What CF Checks | v6.1 Status | v6.2 Status | |
| |-------|---------------|-------------|-------------| |
| | **HTTP Headers** | UA, Sec-CH-UA, Sec-Fetch-* | β
Fixed | β
(now via rquest) | |
| | **TLS Fingerprint (JA3/JA4)** | Cipher suites, extensions, GREASE | β FAILED (rustls) | β
Fixed (BoringSSL) | |
| | **HTTP/2 Fingerprint (Akamai)** | SETTINGS frame values, window size | β FAILED (hyper defaults) | β
Fixed (Chrome H2) | |
|
|
| ### Root Cause |
|
|
| `reqwest` + `rustls` generates a TLS ClientHello that is **nothing like Chrome's**: |
| - `rustls` sends ~6 cipher suites in wrong order. Chrome sends 16 with GREASE. |
| - `rustls` doesn't support `compress_certificate` (brotli). Chrome does. |
| - `rustls` doesn't inject GREASE values. Chrome uses RFC 8701 GREASE. |
| - `hyper`'s HTTP/2 sends `INITIAL_WINDOW_SIZE=65535`. Chrome sends `6291456` (6MB). |
| - `hyper` sends SETTINGS that produce a different Akamai H2 fingerprint. |
|
|
| Cloudflare detects this mismatch **at the TLS handshake level** β before any HTTP headers are even sent. No amount of header faking can fix a wrong JA3. |
|
|
| ### Solution: `rquest` with BoringSSL |
|
|
| Replaced `reqwest` (rustls) with `rquest` (BoringSSL-backed Chrome impersonation): |
|
|
| ```toml |
| # OLD (v6.1): |
| reqwest = { version = "0.12", features = ["rustls-tls", "gzip", "brotli", "cookies", "http2"] } |
| |
| # NEW (v6.2): |
| rquest = { version = "1.0", features = ["full"] } |
| ``` |
|
|
| **`rquest`** uses a patched BoringSSL (Chrome's actual TLS library) that emits a **byte-for-byte identical** TLS ClientHello to Chrome 137. It also configures HTTP/2 SETTINGS to match Chrome's exact values. |
|
|
| One-line initialization: |
| ```rust |
| let client = Client::builder() |
| .impersonate(Impersonate::Chrome137) |
| .cookie_store(true) |
| .build()?; |
| ``` |
|
|
| This single call configures: |
| - β
TLS cipher suite order (16 ciphers + GREASE, exact Chrome order) |
| - β
TLS extension ordering (with compress_certificate, ALPS, GREASE) |
| - β
HTTP/2 SETTINGS frame (INITIAL_WINDOW_SIZE=6291456, etc.) |
| - β
Header ordering (sec-ch-ua before accept, as Chrome does) |
| - β
User-Agent (Chrome 137) |
| - β
GREASE values (random per-connection, as Chrome does) |
| - β
Certificate compression (brotli, zlib) |
| - β
ALPN (h2, http/1.1) |
| |
| ### What This Means in Practice |
| |
| | Site | v6.1 (rustls) | v6.2 (BoringSSL) | |
| |------|---------------|-------------------| |
| | Cloudflare Basic | β οΈ Sometimes | β
Always passes | |
| | Cloudflare JS Challenge | β Blocked | β
Cookie-based pass | |
| | DataDome | β Blocked | β
Passes (JA3 matches) | |
| | PerimeterX/HUMAN | β Blocked | β
Passes (JA3+H2 match) | |
| | Akamai Bot Manager | β Blocked | β
Passes (H2 FP match) | |
| | Cloudflare Turnstile | β Needs WebView | β Still needs WebView | |
| |
| ### Changes |
| |
| **`crates/bex-core/Cargo.toml`**: |
| - Removed `reqwest` with `rustls-tls` feature |
| - Added `rquest = { version = "1.0", features = ["full"] }` |
| - Version bumped to 2.1.0 |
| |
| **`crates/bex-core/src/http_service.rs`**: Complete rewrite: |
| - Uses `rquest::Client` with `Impersonate::Chrome137` |
| - Removed all manual header construction (`browser_default_headers()` deleted) |
| - Headers are now managed by the impersonation layer automatically |
| - Plugin headers still override when explicitly set (for custom Referer, etc.) |
| - Auto-Referer from URL origin still works |
| - Cache logic unchanged |
| |
| ### Build Requirements |
| |
| `rquest` bundles BoringSSL which requires: |
| ```bash |
| # Linux: |
| apt-get install cmake clang |
| |
| # macOS: |
| brew install cmake llvm |
| ``` |
| |
| The build handles BoringSSL compilation automatically via the `boring` crate. |
| |
| ### Migration Notes for Plugin Authors |
| |
| **No changes needed.** The WIT interface (`http::send-request`) is unchanged. |
| Plugins still set their own headers via `Request.headers` β these override |
| the Chrome defaults when explicitly provided. |
| |
| The only behavioral difference: requests that previously got CF-challenged |
| or blocked will now succeed transparently. No code changes needed in plugins. |
| |
| ### Known Limitation |
| |
| Cloudflare Turnstile (interactive CAPTCHA) still requires actual human interaction. |
| This is by design β Turnstile uses browser fingerprinting + proof-of-work that |
| cannot be solved programmatically without a full WebView. |
| |