Kasada Anti-Bot expliqué nécessite de comprendre une stack de détection multicouche qui combine un machine virtuelle bytecode custom, des empreintes TLS/HTTP2, et un scoring de réputation IP agressif. Pour les ingénieurs de scraping senior et les chercheurs en anti-bot, Kasada représente l'un des défis les plus complexes du marché : son script ips.js (~449 KB) embarque une VM propriétaire qui collecte des empreintes navigateur et device avant même que la première requête métier ne parte. Cet article détaille l'architecture, les signaux de détection, et une approche légitime — test autorisé et monitoring de données publiques — utilisant des proxys résidentiels ProxyHat avec un navigateur réel.
Kasada Anti-Bot expliqué : vue d'ensemble de l'architecture
Kasada opère comme un reverse proxy qui s'intercale entre le client et l'origine protégée. Le flux typique se décompose en quatre étapes :
- Pré-challenge réseau : Kasada évalue l'IP, le TLS fingerprint (JA3/JA4) et le HTTP/2 fingerprint avant toute exécution JavaScript.
- Servage du challenge : le serveur renvoie une page HTML contenant
<script src="/ips.js">(ou un chemin obfusqué équivalent). - Exécution de la VM :
ips.jsinstancie une machine virtuelle bytecode custom (~449 KB minifié), décode une table de chaînes encodées, collecte des empreintes navigateur/device, et calcule un checksum d'intégrité basé sur un seed temporel. - Émission du token : la VM génère un cookie
KP_UIDzet une famille d'en-têtesx-kpsdk-ct,x-kpsdk-cd,x-kpsdk-dvqui accompagnent les requêtes suivantes.
Le cookie KP_UIDz sert de jeton de session persistant. Une fois validé par le serveur Kasada, il autorise le passage vers l'origine. Si le token est invalide, expiré, ou généré par une VM altérée, le serveur renvoie un HTTP 429 accompagné d'un en-tête x-kpsdk-ct dont la valeur encode la raison du rejet. Comprendre ce signal est central pour tout kasada bypass légitime : un 429 portant x-kpsdk-ct signifie que le token a échoué côté serveur, pas que l'IP est bloquée.
La VM bytecode de ips.js
Le script ips.js de Kasada ne contient pas de JavaScript lisible. Il embarque un bytecode compact et un interpréteur qui exécute des opérations dans un sandbox custom. Les caractéristiques techniques notables incluent :
- Table de chaînes encodées : les noms d'API DOM (ex.
navigator.webdriver,canvas.toDataURL,WebGLRenderingContext.getParameter) sont XOR-és ou chiffrés avec une clé dérivée du timestamp de la requête, ce qui rend l'analyse statique difficile. - Seed temporel : la VM mesure le temps d'exécution de segments de bytecode et compare le delta à des bornes attendues. Un délai trop court (exécution headless sans rendu) ou trop long (instrumentation de débogage) déclenche un flag.
- Checksum d'intégrité : le script calcule un hash de son propre bytecode en mémoire et l'inclut dans le payload chiffré. Toute modification du script (hooking de fonctions, patch de prototypes) invalide ce checksum.
- Rotation des payloads : chaque exécution produit un payload chiffré différent, même pour le même navigateur. Les en-têtes
x-kpsdk-ct,x-kpsdk-cd,x-kpsdk-dvtransportent ce payload sous forme de tokens base64 de longueur variable.
L'en-tête x-kpsdk-ct (challenge token) est le plus critique : il contient le résultat chiffré de l'exécution de la VM, incluant l'empreinte device et les preuves de calcul. Le serveur Kasada déchiffre ce token, valide le checksum, vérifie la cohérence de l'empreinte avec le TLS fingerprint et l'IP, puis émet ou refuse le KP_UIDz.
Empreintes collectées par la VM
La VM de ips.js kasada collecte un large spectre de signaux navigateur et device. Parmi les plus significatifs :
| Catégorie | Signaux collectés | Utilisation détection |
|---|---|---|
| Canvas | canvas.toDataURL() sur une scène 2D rendue | Hash du rendu — détecte les VM headless et les spoofers naïfs |
| WebGL | getParameter(UNMASKED_VENDOR_WEBGL), RENDERER | Vendor/GPU — incohérent si le User-Agent prétend macOS mais le GPU est VirtualBox |
| AudioContext | Empreinte du traitement audio offline | Détecte les navigateurs modifiés (ex. puppeteer-extra-stealth incomplet) |
| Fonts | Mesure de largeur de glyphes pour N polices | Set de polices installées — signature OS |
| Timing | performance.now(), Date.now() deltas | Détecte l'accélération temporelle et l'absence de rendu |
| Behavioral | Mouvements de souris, événements clavier, scroll | Bot pure-HTTP = aucun signal comportemental |
| Navigator | webdriver, plugins, languages, hardwareConcurrency, deviceMemory | Cohérence avec le User-Agent et le TLS fingerprint |
Le point clé : Kasada ne se fie pas à un seul signal. Elle construit un vecteur d'empreinte multidimensionnel et le croise avec le TLS fingerprint et la réputation IP. Un navigateur qui spoofe navigator.webdriver = false mais dont le JA3 correspond à un client Python requests sera immédiatement flaggé.
Empreintes TLS (JA3/JA4) et HTTP/2 : la première ligne de défense
Avant même que ips.js ne s'exécute, Kasada a déjà pris une décision partielle basée sur la couche transport. Le JA3 fingerprint est un hash calculé à partir des paramètres du ClientHello TLS : version TLS, cipher suites ordonnées, extensions ordonnées, courbes elliptiques, et formats de signature. Le JA4 est une évolution qui sépare ces composants en champs distincts pour faciliter le regroupement. Vous pouvez consulter la spécification détaillée sur le RFC 8446 (TLS 1.3) et des informations complémentaires sur Wikipedia — TLS fingerprinting.
Pourquoi cela compte : chaque runtime HTTP a un JA3 distinct. Un requests.Session() Python produit un JA3 qui ne correspond à aucun navigateur réel. Un curl avec ses valeurs par défaut produit un autre JA3. Kasada maintient une base de signatures connues et bloque ou challenge différemment selon le score :
- JA3 navigateur légitime (Chrome, Firefox, Safari) → pas de challenge ou challenge léger.
- JA3 bibliothèque HTTP (Python urllib, Go net/http, Node.js axios) → challenge systématique ou blocage direct.
- JA3 inconnu ou anonymisé (TLS spoofing naïf) → challenge renforcé, parfois blocage immédiat.
Le HTTP/2 fingerprint ajoute une couche supplémentaire. Kasada inspecte les paramètres SETTINGS du flux HTTP/2 (HEADER_TABLE_SIZE, ENABLE_PUSH, INITIAL_WINDOW_SIZE, etc.), l'ordre des pseudo-headers (:method, :authority, :scheme, :path), et les valeurs de WINDOW_UPDATE. Chrome, Firefox et Safari ont chacun un ordre et des valeurs distincts. Une bibliothèque comme httpx avec http2=True produit un fingerprint HTTP/2 qui ne correspond à aucun navigateur réel, même si le JA3 est spoofé correctement.
La conséquence pratique : pour passer Kasada proprement, il faut soit utiliser un navigateur réel (Playwright, Puppeteer avec Chromium), soit une bibliothèque qui réplique fidèlement le stack TLS + HTTP/2 d'un navigateur (comme curl-impersonate ou tls-client). Les solutions intermédiaires — spoofing partiel du JA3 sans HTTP/2 — échouent systématiquement.
Réputation IP : pourquoi les proxys résidentiels sont essentiels
Kasada accorde un poids considérable à la réputation IP. Contrairement à certains anti-bot qui se fient principalement au challenge JS, Kasada pré-bloque activement les plages d'adresses IP associées aux datacenters et aux ASN cloud connus. Les plages AWS, GCP, Azure, DigitalOcean, OVH, Hetzner, et les principaux hébergeurs VPS sont systématiquement challenge-blocked ou rejetées avant même le servage de ips.js.
Le scoring IP de Kasada prend en compte :
- ASN : les ASN de datacenter (ex. AS14618 Amazon, AS15169 Google) reçoivent un score de méfiance élevé.
- Type de connexion : les IP résidentielles (ASN FAI comme Comcast, Orange, Deutsche Telekom) reçoivent un score de confiance plus élevé.
- Géolocalisation : cohérence entre l'IP géolocalisée et les signaux navigateur (timezone, langue,
Accept-Language). - Historique : Kasada maintient une base d'IP vues sur de multiples sites protégés. Une IP qui a déjà déclenché des challenges sur d'autres clients Kasada hérite d'un score dégradé.
- Volumétrie : un pic de requêtes depuis une même IP ou un même sous-réseau déclenche un rate-limit dynamique.
C'est ici que les proxys résidentiels deviennent indispensables. Un proxy datacenter, même avec un navigateur parfaitement fingerprinté et un ips.js qui s'exécute correctement, recevra un 403 ou un challenge impossible à résoudre parce que l'ASN est sur liste noire. Les proxys mobiles offrent le score de confiance le plus élevé (ASN FAI mobile comme T-Mobile, Vodafone), mais sont plus coûteux et plus lents. Les proxys résidentiels offrent le meilleur rapport coût/fiabilité pour la plupart des cas d'usage.
Approche pratique : ProxyHat résidentiel + navigateur réel
Pour les équipes effectuant du test autorisé ou du monitoring de données publiques sur des sites protégés par Kasada, l'approche la plus fiable consiste à :
- Utiliser un proxy résidentiel ProxyHat pour obtenir une IP de confiance.
- Lancer un navigateur réel via Playwright ou Puppeteer pour que
ips.jss'exécute nativement. - Laisser le challenge se résoudre automatiquement (la VM s'exécute, mint le
KP_UIDzet les en-têtesx-kpsdk-ct). - Récupérer le cookie et les en-têtes pour les réutiliser dans des requêtes HTTP ultérieures si nécessaire, ou continuer dans le navigateur.
Voici un exemple Python avec Playwright utilisant un proxy SOCKS5 ProxyHat :
from playwright.sync_api import sync_playwright
PROXY_SOCKS5 = "socks5://user-country-US:password@gate.proxyhat.com:1080"
def solve_kasada_challenge(target_url: str) -> dict:
with sync_playwright() as p:
browser = p.chromium.launch(
headless=False, # headful recommandé pour Kasada
proxy={"server": PROXY_SOCKS5},
args=[
"--disable-blink-features=AutomationControlled",
"--no-first-run",
"--no-default-browser-check",
],
)
context = browser.new_context(
viewport={"width": 1920, "height": 1080},
locale="en-US",
timezone_id="America/New_York",
user_agent=(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/131.0.0.0 Safari/537.36"
),
)
# Masquer webdriver
context.add_init_script("""
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
});
""")
page = context.new_page()
response = page.goto(target_url, wait_until="networkidle")
# Attendre que KP_UIDz soit posé
page.wait_for_cookie("KP_UIDz", timeout=30000)
cookies = context.cookies()
kp_uidz = next(
(c for c in cookies if c["name"] == "KP_UIDz"), None
)
# Récupérer les en-têtes x-kpsdk-* de la dernière requête
headers = page.evaluate("""
() => {
return performance.getEntriesByType('resource')
.map(e => e.name);
}
""")
browser.close()
return {"kp_uidz": kp_uidz["value"] if kp_uidz else None}
result = solve_kasada_challenge("https://example-protected-site.com")
print(result)
Plusieurs points sont critiques dans cette approche :
- Headful de préférence : Kasada détecte le mode headless via plusieurs signaux (absence de rendu canvas, timing d'exécution anormal). Si le headful n'est pas possible sur votre infrastructure, utilisez
xvfb-runsous Linux pour simuler un display. - Cohérence géographique : l'IP du proxy résidentiel doit correspondre à la timezone et au locale du navigateur. Un proxy US avec un locale
fr-FRet une timezoneEurope/Parisdéclenchera un flag. ProxyHat permet le ciblage par pays et par ville via le username (ex.user-country-USouuser-country-DE-region-state_of_berlin-city-berlin). - Sticky sessions : le challenge
ips.jspeut nécessiter plusieurs requêtes. Si l'IP change entre la requête qui sert le challenge et celle qui soumet le token, Kasada invalide le tout. Utilisez une session sticky ProxyHat :user-sid-abc123:password@gate.proxyhat.com:1080. - Pas de patching de prototypes : hooker
CanvasRenderingContext2D.prototype.getImageDataounavigator.permissions.queryinvalide le checksum d'intégrité de la VM. Les bibliothèques de stealth commepuppeteer-extra-plugin-stealthsont partiellement détectées par Kasada. Préférez un navigateur Chromium non modifié.
Exemple curl pour diagnostic
Pour diagnostiquer si un 429 provient bien de Kasada et comprendre la valeur de x-kpsdk-ct :
curl -x socks5://user-country-US:password@gate.proxyhat.com:1080 \
-s -D - \
-o /dev/null \
"https://example-protected-site.com/"
# Cherchez dans les en-têtes de réponse :
# x-kpsdk-ct: ... (token de challenge)
# x-kpsdk-cd: ... (données de challenge)
# x-kpsdk-dv: ... (version du device)
Un HTTP 429 avec x-kpsdk-ct présent signifie que Kasada a reçu un token mais l'a rejeté. Les causes typiques : IP en liste noire (utilisez un résidentiel), checksum VM invalide (navigateur patché), seed temporel expiré (trop de temps entre le servage et la soumission), ou incohérence TLS/HTTP2 fingerprint (utilisez un vrai navigateur).
Stratégie de rotation et sessions sticky
Avec Kasada, la rotation IP agressive est contre-productive. Le workflow idéal :
- Démarrer une session sticky ProxyHat pour une durée de 10 à 30 minutes.
- Laisser le navigateur résoudre le challenge et mint le
KP_UIDz. - Réutiliser le cookie pour les requêtes suivantes dans la même session.
- Si le cookie expire (typiquement après 1 à 24 heures selon la configuration du site), relancer le navigateur avec une nouvelle session sticky.
La rotation par requête (per-request rotation) ne fonctionne pas avec Kasada car chaque IP doit résoudre son propre challenge. Si vous avez besoin de parallélisme, lancez N navigateurs indépendants, chacun avec sa propre session sticky ProxyHat.
Erreurs courantes et cas limites
Erreur 1 : Spoofing partiel du JA3
Utiliser requests ou httpx avec un custom JA3 via tls-client sans répliquer le HTTP/2 fingerprint complet. Kasada croise JA3 + HTTP/2 + IP reputation. Un JA3 Chrome avec un HTTP/2 fingerprint Go net/http est immédiatement incohérent.
Erreur 2 : Headless sans xvfb
Le mode headless de Chromium ( --headless=new ) produit des signaux de timing et de canvas légèrement différents du mode headful. Kasada exploite ces différences. Solution : xvfb-run -a playwright ... ou conteneur Docker avec Xvfb.
Erreur 3 : Proxy datacenter avec navigateur parfait
Même avec un Chromium non modifié, un TLS parfait et un ips.js qui s'exécute sans erreur, un proxy datacenter recevra un 403. L'ASN est pré-bloqué avant le challenge. C'est l'erreur la plus coûteuse : des heures d'ingénierie de stealth annulées par un choix de proxy inadapté. Consultez les docs ProxyHat pour configurer correctement vos sorties résidentielles.
Erreur 4 : Réutilisation de KP_UIDz cross-IP
Le cookie KP_UIDz est lié à l'IP qui l'a généré. Le réutiliser depuis une autre IP (même résidentielle) déclenche un 429. Si vous devez changer d'IP, régénérez le cookie via un nouveau challenge.
Cas limite : Kasada sur mobile
Sur les sites où Kasada est configuré en mode mobile, le challenge ips.js peut être servi différemment (payload plus léger, détection TouchEvent au lieu de mouse events). Les proxys mobiles ProxyHat (ASN FAI mobile) offrent le score de confiance le plus élevé pour ces cas. Le surcoût est justifié si le site cible filtre agressivement les IP résidentielles non mobiles.
Cadre légal et usage approprié
Les techniques décrites dans cet article sont destinées au test autorisé (pentests avec accord écrit), à la recherche en sécurité, et au monitoring de données publiques conformément aux conditions d'utilisation du site cible. Toute utilisation pour de la fraude, du contournement de paywall, du scalping non autorisé, ou de l'extraction de données personnelles sans base légale est strictement hors du cadre de cet article.
Sous le Computer Fraud and Abuse Act (CFAA) aux États-Unis, l'accès non autorisé à un système informatique peut constituer un délit fédéral. La jurisprudence récente (ex. Van Buren v. United States, 2021) a nuancé la notion d'accès « sans autorisation », mais le scraping en violation explicite des ToS reste risqué. Sous le RGPD en Europe, l'extraction de données personnelles sans base légale (consentement, intérêt légitime) est illégale, indépendamment de la technique utilisée. Consultez les locations ProxyHat pour comprendre la géographie de vos sorties et leurs implications juridiques.
Référez-vous aux tarifs ProxyHat pour évaluer le coût des sessions résidentielles et mobiles selon votre volume. Pour des cas d'usage comme le web scraping légitime ou le SERP tracking, les proxys résidentiels ProxyHat offrent le compromis optimal entre fiabilité et coût.






