pluginengine01 / CHANGELOG-v6.2.md
krystv's picture
docs: add v6.2 changelog for Chrome TLS impersonation via rquest/BoringSSL
b0cb964 verified
|
Raw
History Blame Contribute Delete
4.46 kB

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):

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

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:

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