Décryptage du WAF Captcha de Datadome
Passons en revue, étape par étape, le dernier WAF Captcha/Geetest de Datadome pour comprendre comment accéder aux sites qu'il protège.

Passons en revue, étape par étape, le dernier WAF Captcha/Geetest de Datadome pour comprendre comment accéder aux sites qu'il protège.

Vous avez été bloqué !
Voici la réponse que vous verrez lorsqu'une requête est bloquée par le WAF Captcha de Datadome pendant le scraping d'un site protégé :
<html lang="en">
<head>
<title>datadome.co</title>
<style>
...
</style>
</head>
<body style="margin:0">
<p id="cmsg">Please enable JS and disable any ad blocker</p>
<script data-cfasync="false">
var dd = {
'rt': 'c',
'cid': 'AHrlqAAAAAMAseGpalIFikoAyV2i2w==',
'hsh': '14D062F60A4BDE8CE8647DFC720349',
't': 'fe',
'qp': '',
's': 44330,
'e': 'a1dea4f437d7f7234cf4ab0017373492e7438d099a0632bc3dd4ac787bf0ffa3',
'host': 'geo.captcha-delivery.com',
'cookie': 'Y1XRfldQv9mO1z2TBDqSmbztAi_H6BR04bz14NTujEm~6G_7X6eHyn9i71KhHxsv86WUIZGokIBSyX9bulEARTINIkeWdhjKGUu4Rm7pkbnPpTfSwfC4ntcQPF6rcA5z'
}
</script>
<script data-cfasync="false" src="https://ct.captcha-delivery.com/c.js"></script>
</body>
</html>
À partir du dictionnaire dm renvoyé, le JavaScript lié génère l'URL réelle du défi et l'injecte sous forme d'iframe dans la page. Une fois l'iframe intégrée, vous tomberez soit sur le captcha à résoudre, soit sur un message « You have been blocked ». Cela signifie l'une de deux choses :
- Votre adresse IP est signalée comme mauvaise.
- Votre flux de requêtes ou vos en-têtes s'écartent d'un flux de navigateur normal, ce qui déclenche la détection de bots de Datadome.
Pour éviter cela, testez toujours vos proxies dans un vrai navigateur d'abord, afin de confirmer qu'ils peuvent résoudre le défi et naviguer sur le site, puis reproduisez exactement la même séquence de requêtes et les mêmes en-têtes que cette session de navigateur dans votre script.
La requête
Après avoir résolu le défi, la requête GET suivante est envoyée :

En cas de résolution réussie, un cookie Datadome est renvoyé. La plupart des valeurs de cette requête sont simplement extraites de la page. Le seul élément important est le ddCaptchaEncodedPayload, que nous examinerons et décortiquerons plus loin dans cet article. Il s'agit essentiellement d'un dictionnaire contenant divers détails, appelés signals.

Comment Datadome se présente
« Il ne s'agit pas seulement de distinguer humain ou bot — il s'agit d'intention. Notre IA détecte l'intention malveillante ou légitime en temps réel, qu'elle provienne d'un bot ou d'un humain. »
L'entreprise se présente comme « le leader de la protection contre la cyberfraude » et se targue de bloquer des « milliards » d'attaques en traitant plus d'un trillion de signaux par jour à travers plus de 25 points de présence mondiaux. Elle met en avant son moteur d'IA haute vitesse : par exemple, un communiqué d'avril 2025 affirme qu'il peut « identifier, catégoriser, s'adapter et répondre au trafic en moins de 2 millisecondes ».
« La plupart des outils de détection de bots reposent sur une logique statique à base de règles, incapable de suivre le rythme des menaces émergentes. Le moteur d'IA de DataDome est différent. Il apprend et s'adapte en continu et en temps réel à l'aide de modèles supervisés et non supervisés, … »
Le matériel marketing oppose souvent cela aux défenses historiques ; les articles de blog affirment que les WAF à base de règles sont « archaïques » et « pas de taille face aux bots avancés ».
Dans un billet, DataDome va jusqu'à se qualifier de « seule solution de protection contre les bots et la fraude en ligne livrée sous forme de service ».
Leur PDG affirme que le moteur de machine learning « évalue chaque requête » au lieu de s'appuyer sur des règles statiques (Source : stechcrunch.com).
Ils affirment également prévenir le vol de contenu, les prises de contrôle de comptes, la fraude publicitaire et les attaques DDoS « en temps réel ».
Avant de commencer notre analyse approfondie, gardons à l'esprit que cette entreprise a été financée à hauteur de 81 millions de dollars au total.
Histoire du défi
Cette page WAF a été introduite pour la première fois il y a environ cinq ans, pendant « l'âge d'or des sneaker games ». Lorsqu'elle est apparue sur des sites comme Slamjam et Starcow, le seul moyen de la résoudre était via reCAPTCHA.

Elle a ensuite été remplacée par une implémentation Geetest, puis par un slide captcha de style Geetest personnalisé ou un défi audio.
L'option audio n'a jamais été idéale, car les cookies obtenus de cette manière reçoivent souvent un score de confiance plus faible, ce qui déclenche un second défi ou un blocage rapide.
Cas d'usage
Actuellement, Datadome est encore utilisé par certains distributeurs, la plupart des sites de billetterie des équipes de football européennes, et certains sites de cartes-cadeaux prépayées. Voici quelques exemples :
- https://tickets.rolandgarros.com/
- https://www.pokemoncenter.com/
- https://www.liverpoolfc.com/tickets
- https://mygift.giftcardmall.com/
- https://www.giftcards.com/
- https://www.uefa.com/
- https://www.arsenal.com/
- https://www.vinted.com/
- https://www.thefork.com/
- https://www.etsy.com/
- https://www.soundcloud.com/
- https://www.seatgeek.com/ (et tous les sites qui en dépendent)
Et la liste continue…
Obfuscation du Javascript
À l'intérieur de la page de l'iframe, vous trouverez une longue ligne minifiée contenant le vrai JavaScript du captcha.

Une fois embelli, ce fichier révèle une obfuscation assez simple bâtie sur des techniques à base de tableaux et de fonctions.

Il est plutôt long car il se compose en réalité de huit modules/fichiers distincts, comme le montre l'image ci-jointe.


Chaque fichier remplit un rôle précis et organisé, et contient son propre jeu de fonctions.
Obfuscation des constructions Loop-Switch
Un aspect notable est la manière originale dont ils « obfusquent » les boucles for et les instructions switch. C'est totalement inutile et trivial à inverser, mais je n'avais jamais vu cette technique auparavant.

Ce que ça fait
Au lieu d'écrire :
case 23
le code construit une table de correspondance bidimensionnelle s et pilote l'état du switch via s[x][y], si bien que chaque case ressemble à :
case s[263][471]:
Où la fonction de génération de table ressemble à ceci :
// dynamic table function generation
var s = function (e, t) {
var a, n;
for (t = [], e = 0; e < 128; e++) t[e] = new Array(512);
for (a = 0; a < 512; a++) for (n = 0; n < 128; n++) t[n][a] = t[Ze(128, a, 337, 163, 349, 30, n)];
return t[30];
}();
// and his helper
function Ze(e, t, a, n, c, i, r) {
return (t * c ^ r * n ^ i * a) >>> 0 & e - 1;
}
**Inspecter la table à l'exécution
**Si vous loggez s[yyy][zzz] dans la console de votre navigateur, vous n'obtiendrez pas un nombre, mais un objet Array (une ligne de cette matrice de correspondance).

**Pourquoi c'est une obfuscation idiote
**En exécutant le même générateur de table et les mêmes fonctions de mappage dans un sandbox (par exemple, le module vm de Node), vous pouvez reconstruire l'intégralité de la table de correspondance, ce qui neutralise l'obfuscation.
// 1. Spin up a sandbox and rebuild the exact same table
const vm = require('vm');
const context = vm.createContext({});
vm.runInContext(`
// dynamic table generation and his helper
`, context);
// 2. Build a reverse map from each row object back to its [x,y] indices
const s = context.s;
const rowMap = new Map();
s.forEach((row, x) => {
row.forEach((cell, y) => {
// we key off the *exact* Array instance stored at s[x][y]
rowMap.set(cell, [x, y]);
});
});
// 3. Helper to recover the original numeric state
function stateLocation(x, y) {
return rowMap.get(s[x][y]);
}
// 4. Proof it works:
console.log(stateLocation(263, 471)); // e.g. 42
**Nettoyer le tout
**Une simple traversée d'AST fondée sur cette logique peut remplacer chaque case s[x][y] par le littéral numérique d'origine, restaurant une instruction switch normale.

Rapide et facile, mais une jolie étude de cas.
Dynamicité du script
Il est assez étrange qu'une entreprise fondée avec plus de 80 millions de dollars de financement n'ait toujours pas introduit un script entièrement dynamique par session, comme le font aujourd'hui la plupart des « gros » fournisseurs anti-bot.
Au lieu de cela, ce qu'ils ont fait, c'est mettre en place une rotation quotidienne des fichiers, ce qui signifie que le script de l'iframe change chaque jour à une heure précise.
Cette rotation a été introduite il y a environ un an ; sauf mise à jour ou patch majeur, le numéro de version reste le même, seuls quelques IDs statiques étant modifiés pour identifier le défi exact en cours de résolution.
Noms de signals dynamiques
Il y a quelques mois, un changement majeur a été introduit — d'abord sur le défi Captcha, puis aussi sur l'interstitiel : des clés dynamiques pour les signals. Chaque jour, les clés du dictionnaire de signals changent en une chaîne aléatoire de six caractères. Si vous ne les faites pas correspondre correctement, la résolution sera invalide.

Cela a été une technique astucieuse pour bloquer la plupart des fournisseurs d'API de résolution, mais quelqu'un aurait pu stocker tous les scripts quotidiens « juste au cas où » au cours des cinq dernières années. Il **pourrait ensuite effectuer **une recherche sémantique rapide pour identifier les signals renommés, les entrées ajoutées ou supprimées, ou d'autres modifications. :P

Actuellement, ce mécanisme de clés dynamiques a mis hors jeu la plupart des fournisseurs ou les a forcés à basculer vers des solutions basées sur navigateur, qui sont selon moi la pire façon de livrer un service, en raison de :
- Temps de résolution lents et forte consommation de ressources
- Verrouillage sur des user agents, navigateurs, versions d'OS, timestamps ou fingerprints spécifiques, obligeant les clients à correspondre exactement
- Des développeurs qui n'ont souvent aucune idée de ce que l'anti-bot vérifie, ce qui rend le débogage ou la correction de problèmes propres à un site difficiles
- La nécessité de nouveaux patches de navigateur à chaque mise à jour du défi, entraînant de l'instabilité pour les projets à long terme ou critiques
Tout cela découle du fait de ne pas avoir trouvé de méthode scalable pour scraper et identifier les noms des signals au quotidien.
Défi WASM : boring_challenge

Récemment, un défi basé sur WASM a été introduit dans le cadre de l'un des signals. La fonction principale, appelée boring_challenge, force essentiellement votre navigateur à exécuter en boucle une minuscule machine à états compilée en Rust jusqu'à ce qu'elle recrache un nombre.
- Décoder en Base64 un blob Wasm compact et le compiler de manière synchrone en un WebAssembly.Module.
- Mettre en place les imports wasm-bindgen (agrandir une table externref avec [undefined, null, true, false]).
- Instancier le module (qui met à zéro certaines variables globales via son __wbindgen_start).
- Choisir une graine aléatoire de 32 bits (entre 10 et 20 millions) et un indice de concurrence (le nombre de cœurs de votre CPU).
- Appeler boring_challenge(BigInt(seed), BigInt(concurrency)), qui plonge dans une boucle massive et imbriquée de manipulations de bits, de XOR, de décalages, de rotations et de constantes magiques — le tout codé en dur dans une machine à états à table de correspondance obfusquée.
- Ne sortir que lorsque cette machine à états atteint enfin une valeur terminale, renvoyant un résultat 64 bits que votre script reconvertit en Number.
Il n'y a aucun véritable « captcha » ici, juste de la preuve de travail pure. Le serveur peut à moindre coût réinvoquer la même fonction de son côté pour vérifier que vous avez fait le boulot, mais votre navigateur, lui, doit brûler des cycles CPU pour la résoudre.

C'est trivial à inverser (on le décompile et on l'exécute nous-mêmes). C'est au fond plutôt une « taxe CPU » qu'un véritable fingerprint, peut-être introduite pour créer des problèmes aux navigateurs headless (je suppose ?).
Vous pouvez en apprendre davantage ici, dans mon dépôt open source
Défi de hash dynamique
Après l'introduction du WASM, la dernière « fonctionnalité » introduite est un** défi de hash dynamique**, où certains détails du navigateur, déjà fournis dans d'autres signals, sont assemblés en une liste puis hachés à l'aide d'un hash de défi dynamique spécifique.
La liste résultante alimente ensuite une chaîne dynamique d'opérations qui ressemble à ceci :
((((((inputValue[0] >>> 0 ^ 555683) >>> 0 >> 4 >>> 0)
+ (((956305 & inputValue[1] >>> 0) >>> 0 & (inputValue[2] >>> 0)
+ (inputValue[1] >>> 0) >>> 0) >>> 0) >>> 0)
+ ((2488139776 ^ (inputValue[1] >>> 0 << 1 >>> 0)
+ (inputValue[0] >>> 0 >> 6 >>> 0) >>> 0) >>> 0) >>> 0
^ (((699654 + (inputValue[0] >>> 0) >>> 0) - 1111820 >>> 0)
- ((968080 - (inputValue[0] >>> 0) >>> 0 ^ 864233) >>> 0) >>> 0)
- ((((792526 & inputValue[2] >>> 0) >>> 0
| (298058 & inputValue[1] >>> 0) >>> 0) >>> 0)
- (((inputValue[1] >>> 0) + (inputValue[1] >>> 0) >>> 0
^ 622410 - (inputValue[1] >>> 0) >>> 0) >>> 0) >>> 0) >>> 0) >>> 0
& ((11187856 + (410903 + (inputValue[1] >>> 0) >>> 0) >>> 0)
+ (((inputValue[0] >>> 0 | 0) >>> 0 ^ 706164
- (inputValue[1] >>> 0) >>> 0) >>> 0) >>> 0 << 9 >>> 0)
- (((441672 - (inputValue[2] >>> 0) >>> 0)
+ ((inputValue[0] >>> 0) - (inputValue[2] >>> 0) >>> 0) >>> 0)
+ (((inputValue[1] >>> 0) - 617963 >>> 0)
+ ((inputValue[2] >>> 0) - 798365 >>> 0) >>> 0) >>> 0 >> 8 >>> 0) >>> 0) >>> 0
^ 351641146) >>> 0
C'est une approche intéressante, mais malheureusement assez facile à identifier et à extraire, et comme elle repose uniquement sur des calculs mathématiques, une simple évaluation suffit.

Vérifications d'intégrité du script
À ce jour, aucune vérification d'intégrité n'a été ajoutée, ce qui rend le script facile à patcher à des fins de débogage.
En savoir plus sur les changements de versions
À titre pédagogique, jetez un œil à mes modules de déobfuscation open source pour DataDome, désormais obsolètes (à la fois Interstitial et Captcha) :
- Datadome-Deobfuscator (Oct 2023)
- Datadome-Interstitial-Deobfuscator (Jan 2024)
- Datadome-Captcha-Deobfuscator (Jan 2024)
Résolution d'images

Les images renvoyées par le Captcha de Datadome sont plutôt faciles et rapides à résoudre avec seulement quelques filtres et opérations mathématiques. J'ai déjà rendu open source un script Python de base, toujours fonctionnel, pour démarrer ici : Datadome-GeeTest-Captcha-Solver

Calcul des mouvements
Le vrai défi clé consiste à s'assurer que les mouvements fournis correspondent à ce que DataDome attend.
DataDome analyse les mouvements dans deux listes
- _initialCoordsList qui capture les mouvements depuis le chargement de la page jusqu'au clic sur le bouton de glissement
- _coordsList qui contient les mouvements de glissement
Ces deux listes sont ensuite transformées en 31 signals, basés sur la courbure, la longueur, la rectitude et d'autres métriques, afin de repérer les entrées anormales.

Honnêtement, leurs vérifications sont strictes et efficaces pour stopper la plupart des scripts automatisés basés sur navigateur, même si un bon modèle de ML nourri avec suffisamment de données peut encore faire l'affaire :P.
Pour aider sur cette partie du processus, j'ai rendu open source Datadome-Movements-Display, qui visualise les listes de mouvements brutes avant leur calcul, vous permettant de comparer vos motifs générés à de vraies données de navigateur.
Chiffrement des signals : ddCaptchaEncodedPayload
Lors de l'ajout d'un signal au payload final, une méthode d'encodage personnalisée est utilisée, basée sur le hash du site web.
e(`THcQWT`, i.left)
e(`ds6frg`, i.right)
e(`zzUlTA`, i.up)
e(`uJkDwZ`, i.down);
Aperçu du processus de chiffrement

**Initialisation
**Le système est amorcé avec un hash, un identifiant client (cid) et un salt optionnel. Ces paramètres initialisent un générateur de nombres pseudo-aléatoires (PRNG) qui pilote les étapes suivantes.
**Construction du buffer
**Chaque signal (paire clé-valeur) est obfusqué et passé au XOR avec des octets générés par le PRNG. Des octets marqueurs ({, }) et des séparateurs (:) sont également obfusqués par XOR et ajoutés pour délimiter la structure.
**XOR avec PRNG, 2e passe
**L'ensemble du buffer subit une passe XOR supplémentaire à l'aide d'une seconde séquence PRNG dérivée du cid et du salt. Cette approche à double couche garantit que la valeur finale de chaque octet dépend de manière complexe de multiples paramètres dynamiques.
**Encodage personnalisé de type Base64
**Après le XOR, le buffer est encodé en une chaîne à l'aide d'un algorithme personnalisé de type Base64. Des opérations XOR supplémentaires avec une valeur de salt décrémentée sont appliquées. Un mappage de caractères unique garantit une transmission URL-safe, compliquant encore le déchiffrement.
Le processus de déchiffrement se contente d'inverser ces étapes une fois que vous les avez comprises.
Sans entrer trop dans les détails techniques, pour ne pas perdre les lecteurs peu intéressés, j'ai rendu open source une plongée approfondie dans la logique de chiffrement et de déchiffrement ici, à la fois pour NodeJS et Python :
Alors, comment Datadome pourrait-il être résolu concrètement ?
Passons en revue le processus étape par étape. Je pars du principe que vous utilisez une approche basée sur requests, car une solution basée sur navigateur nécessite un patching constant à chaque mise à jour, une forte consommation de ressources et offre peu de contrôle sur ce qui se passe.
- Identifier et construire l'URL du défi à partir de la réponse initiale.
- Charger la page du défi.
- Extraire les détails requis du défi chargé.
- Extraire, trier et interpréter les clés dynamiques.
- Résoudre le défi d'image.
- **Assembler votre **dictionnaire de signals, les détails du navigateur, les défis résolus et les mouvements calculés.
- Chiffrer le payload.
- L'envoyer à l'endpoint DataDome.
- Récupérer le cookie (si la résolution a réussi).
- Recharger la page cible.
Conclusion
En conclusion, ce n'était qu'une étude de cas. Je ne dis pas que DataDome est un mauvais anti-bot — certaines de leurs techniques sont uniques et intéressantes, même si elles sont faciles à contourner — mais je souligne plutôt à quel point leur WAF est simple et « bon marché », et je m'interroge sur la façon dont les millions reçus de leurs investisseurs ont été dépensés.
Ils devraient peut-être retirer l'article How to Bypass DataDome (And Why It's Not That Simple)
Besoin de solutions de bypass DataDome ?

Si vous avez besoin d'une solution de bypass DataDome fiable pour votre projet, adressez-vous aux experts qui comprennent vraiment la technologie. Mon entreprise, TakionAPI, propose des API professionnelles de bypass anti-bot, dont l'efficacité est prouvée contre DataDome et d'autres systèmes de défense anti-bot.
Vous pouvez consulter et exécuter le fichier d'exemple de la vidéo ici.
Fini de vous soucier de comprendre, d'inverser et de résoudre le défi vous-même, ou de le maintenir à jour chaque jour. Un simple appel d'API fait tout.
Nous proposons des essais gratuits, des exemples d'implémentation et une aide à la configuration pour rendre tout le processus simple et fluide. Consultez notre documentation claire ici, lancez votre essai ici, ou, pour du développement sur mesure et du support, contactez-nous sur Discord.
Rendez-vous sur TakionAPI.tech pour de vraies solutions de bypass anti-bot de haute qualité, on sait ce qu'on fait.
Me contacter
Si cet article vous a plu, suivez-moi sur GitHub et sur Medium pour être notifié à chaque fois que je publie ou rends quelque chose open source.
Sources
- TechCrunch
- Datadome website
- How to Bypass DataDome (And Why It's Not That Simple) — propagande Datadome
- datadome-wasm
- datadome-encryption
- datadome-encryption-python
- Datadome-GeeTest-Captcha-Solver
- Datadome-Movements-Display
- TakionAPI
- TakionAPI DOCS
- Datadome-Deobfuscator (Oct 2023 — obsolète)
- Datadome-Interstitial-Deobfuscator (Jan 2024 — obsolète)
- Datadome-Captcha-Deobfuscator* (Jan 2024 — obsolète)*
- Datadome-Interstital-Encryptor (obsolète)
- How to detect, block and manage DataDome (obsolète)
Sautez l'étape de la rétro-ingénierie.
Takion renvoie des cookies, en-têtes et tokens frais pour tous les grands murs antibot. Un seul POST, aucun navigateur, premier appel dans l'heure.