docs: add v6.2 changelog for Chrome TLS impersonation via rquest/BoringSSL
Browse files- CHANGELOG-v6.2.md +109 -0
CHANGELOG-v6.2.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## v6.2 β Full Chrome TLS Impersonation (BoringSSL)
|
| 2 |
+
|
| 3 |
+
### Critical Fix: Complete Browser Fingerprint Impersonation
|
| 4 |
+
|
| 5 |
+
The v6.1 fixes (Chrome headers, HTTP/2, cookies) were necessary but **not sufficient**.
|
| 6 |
+
Cloudflare uses **three fingerprinting layers simultaneously**, and v6.1 only addressed one:
|
| 7 |
+
|
| 8 |
+
| Layer | What CF Checks | v6.1 Status | v6.2 Status |
|
| 9 |
+
|-------|---------------|-------------|-------------|
|
| 10 |
+
| **HTTP Headers** | UA, Sec-CH-UA, Sec-Fetch-* | β
Fixed | β
(now via rquest) |
|
| 11 |
+
| **TLS Fingerprint (JA3/JA4)** | Cipher suites, extensions, GREASE | β FAILED (rustls) | β
Fixed (BoringSSL) |
|
| 12 |
+
| **HTTP/2 Fingerprint (Akamai)** | SETTINGS frame values, window size | β FAILED (hyper defaults) | β
Fixed (Chrome H2) |
|
| 13 |
+
|
| 14 |
+
### Root Cause
|
| 15 |
+
|
| 16 |
+
`reqwest` + `rustls` generates a TLS ClientHello that is **nothing like Chrome's**:
|
| 17 |
+
- `rustls` sends ~6 cipher suites in wrong order. Chrome sends 16 with GREASE.
|
| 18 |
+
- `rustls` doesn't support `compress_certificate` (brotli). Chrome does.
|
| 19 |
+
- `rustls` doesn't inject GREASE values. Chrome uses RFC 8701 GREASE.
|
| 20 |
+
- `hyper`'s HTTP/2 sends `INITIAL_WINDOW_SIZE=65535`. Chrome sends `6291456` (6MB).
|
| 21 |
+
- `hyper` sends SETTINGS that produce a different Akamai H2 fingerprint.
|
| 22 |
+
|
| 23 |
+
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.
|
| 24 |
+
|
| 25 |
+
### Solution: `rquest` with BoringSSL
|
| 26 |
+
|
| 27 |
+
Replaced `reqwest` (rustls) with `rquest` (BoringSSL-backed Chrome impersonation):
|
| 28 |
+
|
| 29 |
+
```toml
|
| 30 |
+
# OLD (v6.1):
|
| 31 |
+
reqwest = { version = "0.12", features = ["rustls-tls", "gzip", "brotli", "cookies", "http2"] }
|
| 32 |
+
|
| 33 |
+
# NEW (v6.2):
|
| 34 |
+
rquest = { version = "1.0", features = ["full"] }
|
| 35 |
+
```
|
| 36 |
+
|
| 37 |
+
**`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.
|
| 38 |
+
|
| 39 |
+
One-line initialization:
|
| 40 |
+
```rust
|
| 41 |
+
let client = Client::builder()
|
| 42 |
+
.impersonate(Impersonate::Chrome137)
|
| 43 |
+
.cookie_store(true)
|
| 44 |
+
.build()?;
|
| 45 |
+
```
|
| 46 |
+
|
| 47 |
+
This single call configures:
|
| 48 |
+
- β
TLS cipher suite order (16 ciphers + GREASE, exact Chrome order)
|
| 49 |
+
- β
TLS extension ordering (with compress_certificate, ALPS, GREASE)
|
| 50 |
+
- β
HTTP/2 SETTINGS frame (INITIAL_WINDOW_SIZE=6291456, etc.)
|
| 51 |
+
- β
Header ordering (sec-ch-ua before accept, as Chrome does)
|
| 52 |
+
- β
User-Agent (Chrome 137)
|
| 53 |
+
- β
GREASE values (random per-connection, as Chrome does)
|
| 54 |
+
- β
Certificate compression (brotli, zlib)
|
| 55 |
+
- β
ALPN (h2, http/1.1)
|
| 56 |
+
|
| 57 |
+
### What This Means in Practice
|
| 58 |
+
|
| 59 |
+
| Site | v6.1 (rustls) | v6.2 (BoringSSL) |
|
| 60 |
+
|------|---------------|-------------------|
|
| 61 |
+
| Cloudflare Basic | β οΈ Sometimes | β
Always passes |
|
| 62 |
+
| Cloudflare JS Challenge | β Blocked | β
Cookie-based pass |
|
| 63 |
+
| DataDome | β Blocked | β
Passes (JA3 matches) |
|
| 64 |
+
| PerimeterX/HUMAN | β Blocked | β
Passes (JA3+H2 match) |
|
| 65 |
+
| Akamai Bot Manager | β Blocked | β
Passes (H2 FP match) |
|
| 66 |
+
| Cloudflare Turnstile | β Needs WebView | β Still needs WebView |
|
| 67 |
+
|
| 68 |
+
### Changes
|
| 69 |
+
|
| 70 |
+
**`crates/bex-core/Cargo.toml`**:
|
| 71 |
+
- Removed `reqwest` with `rustls-tls` feature
|
| 72 |
+
- Added `rquest = { version = "1.0", features = ["full"] }`
|
| 73 |
+
- Version bumped to 2.1.0
|
| 74 |
+
|
| 75 |
+
**`crates/bex-core/src/http_service.rs`**: Complete rewrite:
|
| 76 |
+
- Uses `rquest::Client` with `Impersonate::Chrome137`
|
| 77 |
+
- Removed all manual header construction (`browser_default_headers()` deleted)
|
| 78 |
+
- Headers are now managed by the impersonation layer automatically
|
| 79 |
+
- Plugin headers still override when explicitly set (for custom Referer, etc.)
|
| 80 |
+
- Auto-Referer from URL origin still works
|
| 81 |
+
- Cache logic unchanged
|
| 82 |
+
|
| 83 |
+
### Build Requirements
|
| 84 |
+
|
| 85 |
+
`rquest` bundles BoringSSL which requires:
|
| 86 |
+
```bash
|
| 87 |
+
# Linux:
|
| 88 |
+
apt-get install cmake clang
|
| 89 |
+
|
| 90 |
+
# macOS:
|
| 91 |
+
brew install cmake llvm
|
| 92 |
+
```
|
| 93 |
+
|
| 94 |
+
The build handles BoringSSL compilation automatically via the `boring` crate.
|
| 95 |
+
|
| 96 |
+
### Migration Notes for Plugin Authors
|
| 97 |
+
|
| 98 |
+
**No changes needed.** The WIT interface (`http::send-request`) is unchanged.
|
| 99 |
+
Plugins still set their own headers via `Request.headers` β these override
|
| 100 |
+
the Chrome defaults when explicitly provided.
|
| 101 |
+
|
| 102 |
+
The only behavioral difference: requests that previously got CF-challenged
|
| 103 |
+
or blocked will now succeed transparently. No code changes needed in plugins.
|
| 104 |
+
|
| 105 |
+
### Known Limitation
|
| 106 |
+
|
| 107 |
+
Cloudflare Turnstile (interactive CAPTCHA) still requires actual human interaction.
|
| 108 |
+
This is by design β Turnstile uses browser fingerprinting + proof-of-work that
|
| 109 |
+
cannot be solved programmatically without a full WebView.
|