| name | tls-fingerprint-impersonation |
| description | Spoof TLS ClientHello and JA4 fingerprints for browser impersonation. |
| version | 1.0.0 |
| author | uphiago |
| license | MIT |
| platforms | ["linux"] |
| compatibility | Requires python3, pip, or rust toolchain |
| metadata | {"tags":["recon","TLS","JA3","JA4","fingerprint","impersonation","HTTP","browser"],"category":"recon","related_skills":["http2-header-impersonation","stealth-browser-launch","humanize-automation"]} |
TLS Fingerprint Impersonation
Spoof TLS ClientHello parameters — cipher suites, key exchange groups, signature algorithms, and extension order — to match real browsers at the JA3/JA4 fingerprint level. Uses patched rustls to rebuild the TLS layer with browser-identical configurations. Bypasses TLS fingerprinting detection (Cloudflare, Akamai, F5) that flags non-browser TLS stacks. Supports 20 browser profiles including Chrome 100-142, Firefox 128-144, Safari iOS 18, and OkHttp 3-5 (Android).
When to Use
- Target returns 403/blocked on curl/httpx even with correct User-Agent headers.
- Cloudflare or Akamai is fingerprinting TLS ClientHello (JA3/JA4 mismatch with browser).
- API probing requires mobile-app impersonation (OkHttp fingerprint for Android).
- Need high-throughput HTTP requests that pass TLS fingerprint checks without running a full browser.
- Target shows different behavior based on TLS fingerprint (mobile vs desktop endpoints).
Prerequisites
terminal tool with python3.
- Python:
pip install impit (wraps the Rust library via PyO3).
- Or Node.js:
npm install impit (native binding).
- Or Rust:
impit crate with patched dependencies in Cargo.toml.
Quick Detection
curl -sk https://target.com | head -1
python3 -c "
from impit import Impit
impit = Impit.builder().with_fingerprint('chrome142').build()
r = impit.get('https://target.com').text
print(r[:200])
"
Procedure
Phase 1 — Browser Profile Selection
Choose the right fingerprint for your target:
| Profile | Use case | Key differentiator |
|---|
chrome142 | Modern desktop | Latest Chrome, post-quantum KEX (X25519MLKEM768), GREASE |
chrome100 | Legacy systems | Older cipher suites, no GREASE in key exchange |
firefox144 | Firefox desktop | Different pseudo-header order, FFDHE groups, SHA-1 signatures |
safari_ios18 | iOS mobile | 3DES ciphers, duplicate signature algorithm, no session tickets |
okhttp4 | Android apps | BoringSSL profile, no GREASE, no ECH, simpler cipher suites |
chrome124 | Common default | Good balance of modern compatibility and detection pass rate |
from impit import Impit
client = Impit.builder().with_fingerprint("chrome142").build()
client = Impit.builder().with_fingerprint("firefox144").build()
client = Impit.builder().with_fingerprint("ios18").build()
client = Impit.builder().with_fingerprint("okhttp4").build()
Phase 2 — Basic Request
from impit import Impit
impit = (
Impit.builder()
.with_fingerprint("chrome142")
.with_ignore_tls_errors(True)
.with_http3()
.with_fallback_to_vanilla(True)
.build()
)
response = impit.get("https://target.com")
print(response.status_code)
print(response.headers)
print(response.text()[:500])
Phase 3 — With Proxy
impit = (
Impit.builder()
.with_fingerprint("chrome142")
.with_proxy("http://user:pass@residential-proxy:8080")
.with_default_timeout(15_000)
.build()
)
response = impit.get("https://target.com/api/endpoint")
Phase 4 — Custom Headers
response = impit.get(
"https://target.com/api/v1",
headers={
"X-Forwarded-For": "1.2.3.4",
"Authorization": "Bearer token",
}
)
Phase 5 — Cookie Session
from impit.cookie import Jar
impit = (
Impit.builder()
.with_fingerprint("chrome142")
.with_cookie_store(Jar())
.build()
)
impit.post("https://target.com/login", json={
"username": "admin", "password": "admin"
})
response = impit.get("https://target.com/dashboard")
Phase 6 — Fingerprint Selection Logic
Choose based on target characteristics:
def select_fingerprint(target_url):
"""Auto-select browser fingerprint based on target."""
if "mobile" in target_url or "api/v2" in target_url:
return "ios18"
elif "android" in target_url or "play.google" in target_url:
return "okhttp4"
elif target_url.startswith("https://"):
return "chrome142"
return "chrome124"
Phase 7 — JA4 Hash Validation
Verify your TLS fingerprint is working correctly:
python3 -c "
from impit import Impit
impit = Impit.builder().with_fingerprint('chrome142').build()
r = impit.get('https://cloudflare.com/cdn-cgi/trace')
print(r.text())
# Look for JA4 hash in trace output or response headers
"
Browser Fingerprint Reference
TLS Configuration per Browser
| Feature | Chrome 142 | Firefox 144 | Safari iOS 18 | OkHttp 4 |
|---|
| TLS versions | 1.3 + 1.2 | 1.3 + 1.2 | 1.3 + 1.2 | 1.3 + 1.2 |
| GREASE cipher | ✅ (pos 1) | ❌ | ✅ | ❌ |
| GREASE key exchange | ✅ (pos 1) | ❌ | ✅ | ❌ |
| Post-quantum (MLKEM768) | ✅ | ✅ | ✅ | ❌ |
| FFDHE groups | ❌ | ✅ (2048/3072) | ❌ | ❌ |
| SHA-1 signatures | ❌ | ✅ | ✅ (legacy) | ✅ (RSA only) |
| ECH GREASE | ✅ | ✅ | ❌ | ❌ |
| 3DES ciphers | ❌ | ❌ | ✅ | ❌ |
| Certificate compression | Brotli | Zlib+Brotli+Zstd | Zlib | ❌ |
| Delegated credentials | ❌ | ✅ | ❌ | ❌ |
| Session tickets | ✅ | ✅ | ❌ | ✅ |
| Duplicate signatures | ❌ | ❌ | ✅ (RsaPssRsaSha384) | ❌ |
HTTP/2 Settings per Browser
| Browser | Stream Window | Connection Window | Pseudo-Header Order |
|---|
| Chrome | 6,291,456 | 15,663,105 | :method :authority :scheme :path |
| Firefox | 131,072 | 12,517,377 | :method :path :authority :scheme |
| Safari iOS | 2,097,152 | 10,485,760 | :method :scheme :authority :path |
| OkHttp | 16,777,216 | 16,777,216 | :method :path :authority :scheme |
Multipart Boundary Format
| Browser | Format | Example |
|---|
| Chrome | ----WebKitFormBoundary + 16 alphanumeric | ----WebKitFormBoundaryx8fH3kLm9pQr2sTv |
| Firefox | ----geckoformboundary + hex u64 values | ----geckoformboundary3fa8c10e5d6b2904 |
| OkHttp | UUID v4 | 550e8400-e29b-41d4-a716-446655440000 |
Pitfalls
- Not all sites use TLS fingerprinting. Test with curl first — if it works, TLS fingerprinting is not the blocker.
- Fingerprint must match User-Agent. Using Chrome TLS with Firefox UA headers will be detected.
- HTTP/1.1-only sites don't use HTTP/2 impersonation. The
with_http3() flag only matters for sites that support it.
- OkHttp 3 is TLS 1.2 only. Some modern servers reject TLS 1.2 connections.
- TLS fingerprint caching means first request is slowest.
CryptoProvider instances are cached per fingerprint — subsequent requests are fast.
- Vanilla fallback may leak your real TLS fingerprint. Disable
vanilla_fallback if stealth is critical.
Verification
- Confirm TLS fingerprint matches target browser using
https://www.howsmyssl.com/ or Cloudflare trace endpoint.
- Check
cf-ja4 response header when hitting Cloudflare-protected sites.
- Verify response status changes from 403 → 200 when using browser fingerprint vs vanilla curl.
- Test with multiple browser profiles to find the one that passes the target's detection.
Related Skills
http2-header-impersonation — HTTP/2 pseudo-header ordering and SETTINGS frame matching.
stealth-browser-launch — Full browser automation with C++ fingerprint patches for JS-heavy targets.
humanize-automation — Human-like interaction patterns for behavioral detection bypass.