## 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.