
Un proxy MITM local qui vous permet de contrôler les empreintes TLS (JA3/JA4), les empreintes HTTP/2, l'ordre des en-têtes HTTP et le User-Agent — le tout à partir d'un seul fichier de configuration YAML.
Un proxy MITM local qui vous permet de contrôler les empreintes TLS (JA3/JA4), les empreintes HTTP/2, l'ordre des en-têtes HTTP, le User-Agent et les en-têtes d'adresse IP source — le tout à partir d'un seul fichier de configuration YAML.
Une extension Chrome est incluse pour activer/désactiver le proxy et changer de profil d'empreinte directement depuis la barre d'outils du navigateur, sans redémarrer le proxy.
Conçu pour les tests de sécurité autorisés des systèmes de détection de bots WAF. Acheminez curl, des navigateurs ou Playwright via le proxy pour observer comment différentes combinaisons d'empreintes sont classifiées.
curl / browser / Playwright
│ HTTP CONNECT (to proxy)
▼
┌─────────────────────────────────────────┐
│ impersonate-proxy │
│ │
│ MITM TLS ◄──────────────► uTLS │
│ (our CA cert) (custom JA3/4) │
│ │
│ Header rewriter (UA, order, add/del) │
│ HTTP/2 framer (SETTINGS, WINDOW_UPDATE│
│ pseudo-header order) │
└─────────────────────────────────────────┘
│ Custom TLS ClientHello + HTTP/2
▼
Target server / WAF
| Couche | Ce que vous pouvez contrôler |
|---|---|
| TLS | Suites de chiffrement, extensions, leur ordre (JA3 / JA4) via les préréglages uTLS ou une spécification entièrement personnalisée |
brew install go
Le Go fourni par la distribution est souvent obsolète. Installez directement le binaire officiel :
# Download and extract (replace 1.22.5 with the latest from https://go.dev/dl/)
curl -OL https://go.dev/dl/go1.22.5.linux-amd64.tar.gz
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.22.5.linux-amd64.tar.gz
# Add to PATH (add this line to ~/.bashrc or ~/.zshrc to make it permanent)
export PATH=$PATH:/usr/local/go/bin
Vérification :
go version
# go version go1.22.5 linux/amd64
ARM64 (Raspberry Pi, AWS Graviton, etc.) : remplacez
linux-amd64parlinux-arm64dans l'URL de téléchargement.
git clone https://github.com/ytkoka/impersonate-proxy.git
cd impersonate-proxy
make build
Le CA est généré automatiquement au premier lancement. Démarrez le proxy une fois pour créer ca.crt et ca.key :
make run
# 2026/04/22 12:00:00 generated CA certificate → ca.crt
# 2026/04/22 12:00:00 listening on 127.0.0.1:8080 preset=chrome
Arrêtez-le avec Ctrl-C.
Les clients doivent approuver votre CA MITM afin de ne pas rejeter les certificats feuilles générés par le proxy.
Trousseau système macOS (affecte toutes les applications) :
make trust-ca # runs: sudo security add-trusted-cert ...
Confiance système Linux (affecte toutes les applications ; nécessite le paquet ca-certificates) :
# Debian / Ubuntu
sudo cp ca.crt /usr/local/share/ca-certificates/impersonate-proxy.crt
sudo update-ca-certificates
# RHEL / Fedora / Amazon Linux
sudo cp ca.crt /etc/pki/ca-trust/source/anchors/impersonate-proxy.crt
sudo update-ca-trust
curl uniquement (aucun changement au niveau système) :
curl --cacert ca.crt ...
Playwright / Node.js :
export NODE_EXTRA_CA_CERTS="$(pwd)/ca.crt"
Firefox : Préférences → Vie privée et sécurité → Certificats → Autorités → Importer ca.crt
Modifiez config.yaml avant de démarrer le proxy. Tous les champs ont des valeurs par défaut — vous ne devez spécifier que ce que vous souhaitez remplacer.
listen: "127.0.0.1:8080"
mgmt_listen: "127.0.0.1:8081" # management API used by the Chrome extension (empty to disable)
ca_cert: "ca.crt"
ca_key: "ca.key"
tls:
# TLS fingerprint preset (controls JA3 / JA4)
# Options: chrome | firefox | safari | edge | ios | random | golang
preset: "chrome"
http:
# Override User-Agent (leave empty to pass through the client's UA)
user_agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
# Spoof source IP: sets both X-Forwarded-For and True-Client-IP to this value,
# replacing any values the client may have already set (leave empty to disable)
# client_ip: "1.2.3.4"
# Emit headers in this order; headers not listed are appended after
header_order:
- "Host"
- "User-Agent"
- "Accept"
- "Accept-Language"
- "Accept-Encoding"
- "Connection"
# Add or overwrite headers
add_headers:
Accept-Language: "ja,en-US;q=0.9,en;q=0.8"
# Remove headers before forwarding
remove_headers: []
http2:
enabled: true
# SETTINGS frame entries — id and order both affect the HTTP/2 fingerprint.
# RFC 7540 §11.3 IDs:
# 1=HEADER_TABLE_SIZE 2=ENABLE_PUSH 3=MAX_CONCURRENT_STREAMS
# 4=INITIAL_WINDOW_SIZE 5=MAX_FRAME_SIZE 6=MAX_HEADER_LIST_SIZE
settings:
- { id: 1, val: 65536 } # Chrome defaults shown here
- { id: 2, val: 0 }
- { id: 4, val: 6291456 }
- { id: 6, val: 262144 }
# Connection-level WINDOW_UPDATE increment
window_update: 15663105
# Order of pseudo-headers in the HEADERS frame
pseudo_header_order: [method, authority, scheme, path]
Au démarrage, le proxy expose également une API HTTP légère sur mgmt_listen (par défaut 127.0.0.1:8081). L'extension Chrome l'utilise pour lire et mettre à jour les paramètres à l'exécution sans redémarrer le proxy. Vous pouvez aussi l'appeler directement avec curl :
| Endpoint | Méthode | Description |
|---|---|---|
/api/config |
# Read current settings
curl http://127.0.0.1:8081/api/config
# Switch to Firefox fingerprint and set a spoofed IP
curl -s -X POST http://127.0.0.1:8081/api/config \
-H "Content-Type: application/json" \
-d '{"tls_preset":"firefox","client_ip":"203.0.113.1","user_agent":""}'
# Switch to an arbitrary JA3/JA4 fingerprint at runtime — same fields as the
# config.yaml custom_hello block, sent as JSON (see "Custom TLS fingerprint" below)
curl -s -X POST http://127.0.0.1:8081/api/config \
-H "Content-Type: application/json" \
-d '{
"tls_preset": "custom",
"custom_hello": {
"cipher_suites": [2570, 4865, 4866, 4867, 49195, 49199, 49196, 49200, 52393, 52392, 49171, 49172, 156, 157, 47, 53],
"curves": ["X25519", "P256", "P384"],
"versions": ["1.3", "1.2"],
"extensions": [2570, 0, 23, 65281, 10, 11, 35, 16, 5, 18, 13, 51, 45, 43, 27, 21]
},
"client_ip": "",
"user_agent": ""
}'
Les modifications prennent effet immédiatement pour les nouvelles connexions. Définissez mgmt_listen: "" pour désactiver entièrement l'API.
preset: "custom")Les préréglages intégrés (chrome, firefox, safari, …) couvrent les cas les plus courants. Lorsque vous devez correspondre à une version spécifique d'un navigateur ou à une empreinte différente de ces préréglages, définissez preset: "custom" et fournissez un bloc custom_hello.
Correspondance entre JA3 / JA4 et les champs de configuration
JA3 et JA4 sont des hachages unidirectionnels — vous ne pouvez pas inverser un hachage pour retrouver la spécification. Trouvez les paramètres sous-jacents du navigateur cible avec tls.peet.ws ou Wireshark, puis collez-les dans
custom_hello.
Exemple Chrome 131
tls:
preset: "custom"
custom_hello:
cipher_suites: # hex IDs; 0x0a0a = GREASE placeholder (randomised per connection)
- 0x0a0a
- 0x1301 # TLS_AES_128_GCM_SHA256
- 0x1302 # TLS_AES_256_GCM_SHA384
- 0x1303 # TLS_CHACHA20_POLY1305_SHA256
- 0xc02b # ECDHE-ECDSA-AES128-GCM-SHA256
- 0xc02f # ECDHE-RSA-AES128-GCM-SHA256
- 0xc02c # ECDHE-ECDSA-AES256-GCM-SHA384
- 0xc030 # ECDHE-RSA-AES256-GCM-SHA384
- 0xcca9 # ECDHE-ECDSA-CHACHA20-POLY1305
- 0xcca8 # ECDHE-RSA-CHACHA20-POLY1305
- 0xc013 # ECDHE-RSA-AES128-SHA
- 0xc014 # ECDHE-RSA-AES256-SHA
- 0x009c # RSA-AES128-GCM-SHA256
- 0x009d # RSA-AES256-GCM-SHA384
- 0x002f # RSA-AES128-SHA
- 0x0035 # RSA-AES256-SHA
curves: # X25519 | X25519Kyber768 | P256 | P384 | P521
- "X25519Kyber768"
- "X25519"
- "P256"
versions: # TLS versions to advertise
- "1.3"
- "1.2"
extensions: # extension type IDs in order (controls JA3 extensions component)
- 0x0a0a # GREASE
- 0 # server_name (SNI)
- 23 # extended_master_secret
- 65281 # renegotiation_info
- 10 # supported_groups
- 11 # ec_point_formats
- 35 # session_ticket
- 16 # ALPN
- 5 # status_request
- 18 # signed_certificate_timestamp
- 13 # signature_algorithms
- 51 # key_share
- 45 # psk_key_exchange_modes
- 43 # supported_versions
- 27 # compress_certificate
- 17513 # application_settings (ALPS)
- 0x0a0a # GREASE
- 21 # padding
IDs de types d'extension pris en charge
Mises à jour à l'exécution :
preset: "custom"n'est pas limité àconfig.yaml— il peut également être activé à l'exécution via l'API de gestion (POST /api/configavec un objetcustom_hello, voir API de gestion) ou depuis la liste déroulante TLS Preset de l'extension Chrome, sans redémarrer le proxy.
make run
# Kills any previous instance on port 8080, rebuilds, and starts.
Pour changer de profil d'empreinte, modifiez config.yaml et relancez make run.
# With CA trusted system-wide (after make trust-ca):
curl --proxy http://127.0.0.1:8080 https://tls.peet.ws/api/all
# Without system trust — pass CA explicitly:
curl --proxy http://127.0.0.1:8080 --cacert ca.crt https://tls.peet.ws/api/all
Le répertoire chrome-extension/ contient une extension Manifest V3 qui contrôle le proxy depuis la barre d'outils du navigateur.
Installation :
chrome://extensions dans Chromechrome-extension/Contrôles :
Portée du User-Agent : L'extension ne modifie que l'en-tête HTTP
User-Agent. Lenavigator.userAgentde JavaScript est contrôlé par Chrome lui-même et n'est pas affecté. Pour usurper les deux simultanément, lancez Chrome avec--user-agent="..."en plus des paramètres du proxy.
const { chromium } = require('playwright');
const browser = await chromium.launch();
const context = await browser.newContext({
proxy: { server: 'http://127.0.0.1:8080' },
});
// If CA is not in the system keychain, set before launching:
// NODE_EXTRA_CA_CERTS=./ca.crt node script.js
const page = await context.newPage();
await page.goto('https://tls.peet.ws/api/all');
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
context = browser.new_context(proxy={"server": "http://127.0.0.1:8080"})
page = context.new_page()
page.goto("https://tls.peet.ws/api/all")
Définissez NODE_EXTRA_CA_CERTS (Node) ou REQUESTS_CA_BUNDLE (Python) si le CA n'est pas approuvé au niveau système.
tls.peet.ws renvoie la décomposition complète de l'empreinte pour toute requête qu'il reçoit. Passez la sortie dans jq ou Python pour une vue lisible :
curl -s --proxy http://127.0.0.1:8080 --cacert ca.crt \
https://tls.peet.ws/api/all | python3 -m json.tool
Champs clés à vérifier :
impersonate-proxy/
├── main.go # Entry point
├── config/config.go # YAML config struct and defaults
├── fp/dialer.go # uTLS dialer — TLS fingerprint presets
├── h2fp/conn.go # HTTP/2 framer — SETTINGS / WINDOW_UPDATE / pseudo-header control
├── mitm/ca.go # MITM CA: generate, cache, and serve leaf certs
├── proxy/proxy.go # Proxy server: CONNECT handling, protocol branch, runtime config
├── rewrite/headers.go # HTTP header rewriting (UA, order, add/remove, IP spoof)
├── mgmt/server.go # Management HTTP API (/api/config GET + POST)
├── chrome-extension/
│ ├── manifest.json # Manifest V3
│ ├── popup.html # Toolbar popup UI
│ ├── popup.css
│ ├── popup.js # Proxy toggle + management API client
│ └── icon.svg
├── config.yaml # Default configuration
└── Makefile
Supprimez le binaire et les fichiers CA générés :
make clean
Si vous avez ajouté le CA au trousseau système macOS, supprimez-le via Trousseau d'accès (recherchez « impersonate-proxy CA ») ou :
sudo security delete-certificate -c "impersonate-proxy CA" /Library/Keychains/System.keychain
Transfer-Encoding: chunked ne sont actuellement pas prises en charge.User-Agent, mais le navigator.userAgent de JavaScript est défini indépendamment par le navigateur et n'est pas affecté. Utilisez l'option de lancement --user-agent de Chrome pour remplacer les deux simultanément.Cet outil est destiné uniquement aux tests de sécurité autorisés — par exemple, tester les configurations WAF et de détection de bots sur des systèmes qui vous appartiennent ou pour lesquels vous disposez d'une autorisation écrite explicite.
L'utilisation de cet outil contre des systèmes sans autorisation peut violer les lois applicables (telles que le Computer Fraud and Abuse Act, la loi japonaise relative à l'accès non autorisé aux ordinateurs, ou la législation équivalente de votre juridiction) ainsi que les conditions d'utilisation de la cible.
Les auteurs déclinent toute responsabilité en cas d'utilisation abusive.
custom_hello| HTTP/1.1 | Ordre des en-têtes, User-Agent, ajout/suppression de tout en-tête, usurpation d'IP (X-Forwarded-For / True-Client-IP) |
| HTTP/2 | Valeurs et ordre de SETTINGS, WINDOW_UPDATE, ordre des pseudo-en-têtes (empreinte HTTP/2) |
GET |
Renvoie les paramètres actifs au format JSON, y compris le custom_hello actuel |
/api/config | POST | Met à jour le préréglage TLS (y compris un custom_hello entièrement personnalisé), l'IP client et le User-Agent |
| Navigateur | Préréglage TLS | SETTINGS HTTP/2 | WINDOW_UPDATE |
|---|
| Chrome | chrome | 1:65536,2:0,4:6291456,6:262144 | 15663105 |
| Firefox | firefox | 1:65536,4:131072,5:16384 | 12517377 |
| Safari | safari | 1:4096,3:100,4:2097152,6:16384 | 10485760 |
| Composant de l'empreinte | Champ de configuration | Notes |
|---|
| Plage de versions TLS | versions | Min/max dérivés automatiquement |
| Liste et ordre des suites de chiffrement | cipher_suites | Utilisez 0x0a0a comme espace réservé GREASE ; uTLS le randomise à chaque connexion |
| IDs et ordre des types d'extension | extensions | L'ordre contrôle directement le composant extensions de JA3 ; les valeurs correspondant au motif GREASE (0xXAXA) sont randomisées à chaque connexion |
| Groupes pris en charge (courbes) | curves | Contrôle également les key shares envoyés |
| ID | Nom | Notes |
|---|
0xXAXA (tout motif GREASE) | GREASE | Randomisé à chaque connexion |
0 | server_name (SNI) | |
5 | status_request | OCSP stapling |
10 | supported_groups | Utilise la liste curves |
11 | ec_point_formats | Fixe : uncompressed (0) |
13 | signature_algorithms | Valeurs par défaut type Chrome |
16 | ALPN | Annonce h2, http/1.1 |
18 | signed_certificate_timestamp | |
21 | padding | Padding style BoringSSL |
23 | extended_master_secret | |
27 | compress_certificate | |
28 | record_size_limit | Fixe : 0x4001 |
35 | session_ticket | |
43 | supported_versions | Utilise la liste versions |
45 | psk_key_exchange_modes | PSK avec DHE |
50 | signature_algorithms_cert | Valeurs par défaut type Chrome |
51 | key_share | Key shares pour X25519 et P256 (depuis curves) |
17513 | application_settings (ALPS) | Annonce h2 |
65281 | renegotiation_info | |
| autre | GenericExtension | Envoyé avec une charge utile vide |
| Contrôle | Fonction |
|---|
| Bascule du proxy | Active / désactive le réglage du proxy de Chrome (achemine le trafic via :8080) |
| Préréglage TLS | Change le préréglage d'empreinte uTLS (chrome / firefox / safari / edge / ios / random / golang / custom) |
| Suites de chiffrement / Courbes / Versions TLS / Extensions | Affichés lorsque Custom (JA3/JA4) est sélectionné — les mêmes champs que custom_hello dans config.yaml, vous permettant de régler une empreinte JA3/JA4 arbitraire sans modifier le YAML ni redémarrer le proxy |
| IP client | Définit X-Forwarded-For et True-Client-IP sur chaque requête |
| User-Agent | Remplace l'en-tête HTTP User-Agent |
| Bouton Appliquer | Envoie les nouveaux paramètres à l'API de gestion ; prend effet immédiatement |
| Champ API | Adresse de l'API de gestion (par défaut http://127.0.0.1:8081) |
| Champ | Description |
|---|
tls.ja3_hash | Hachage de l'empreinte JA3 |
tls.ja4 | Chaîne d'empreinte JA4 |
http2.akamai_fingerprint | Chaîne d'empreinte HTTP/2 (SETTINGS + WINDOW_UPDATE + ordre des pseudo-en-têtes) — le nom du champ est défini par l'API tls.peet.ws |
http1.headers | Noms des en-têtes dans l'ordre reçu par le serveur |
user_agent | User-Agent tel que vu par le serveur |
ip | Adresse IP source telle que vue par le serveur — vérifiez ici l'usurpation X-Forwarded-For / True-Client-IP |
| Cible | Description |
|---|
make build | Compile le binaire |
make run | Compile, tue toute instance existante, puis démarre |
make trust-ca | Ajoute ca.crt au trousseau système macOS (nécessite sudo) |
make clean | Supprime le binaire, ca.crt et ca.key |