Desvendando o WAF de Captcha do Datadome
Vamos percorrer, passo a passo, o mais recente WAF de Captcha/Geetest do Datadome para entender como acessar sites protegidos por ele.

Vamos percorrer, passo a passo, o mais recente WAF de Captcha/Geetest do Datadome para entender como acessar sites protegidos por ele.

Você foi bloqueado!
Abaixo está a resposta que você vê quando uma requisição é bloqueada pelo WAF de Captcha do Datadome ao fazer scraping de um site protegido:
<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>
Usando o dicionário dm retornado, o JavaScript vinculado gera a URL real do desafio e a injeta como um iframe na página. Uma vez que o iframe é embutido, você vai encontrar ou o captcha para resolver ou uma mensagem "You have been blocked". Isso significa uma de duas coisas:
- Seu endereço de IP está marcado como ruim.
- Seu fluxo de requisição ou headers desviam de um fluxo normal de navegador, disparando a detecção de bots do Datadome.
Para evitar isso, sempre teste seus proxies em um navegador real primeiro, para confirmar que eles conseguem resolver o desafio e navegar pelo site, e espelhe exatamente a mesma sequência de requisições e headers daquela sessão de navegador no seu script.
A requisição
Após resolver o desafio, a seguinte requisição GET é enviada:

Ao resolver com sucesso, um cookie Datadome é retornado. A maioria dos valores nessa requisição são simplesmente extraídos da página. O único elemento importante é o ddCaptchaEncodedPayload, que vamos examinar e detalhar mais adiante neste artigo. Ele é essencialmente um dicionário contendo vários detalhes, chamados de sinais.

Como o Datadome se promove
"It's not just about human or bot — it's about intent. Our AI detects malicious vs. legitimate intent in real time, whether it is from a bot or a human."
A empresa se autodenomina "the leader in cyberfraud protection" e se gaba de bloquear "bilhões" de ataques processando mais de um trilhão de sinais por dia em mais de 25 pontos de presença globais. Ela foca na sua engine de IA de alta velocidade: por exemplo, um comunicado de abril de 2025 afirma que ela consegue "identify, categorize, adapt, and respond to traffic in less than 2 milliseconds."
"Most bot detection tools rely on static, rule-based logic that can't keep pace with emerging threats. DataDome's AI engine is different. It continuously learns and adapts in real time using both supervised and unsupervised models, …"
O material de marketing frequentemente contrasta isso com defesas legadas; blogs afirmam que WAFs baseados em regras são "antiquated" e "no match for advanced bots."
Em um blog, o DataDome chega a se chamar de "the only bot and online fraud protection solution delivered as a service."
O CEO deles afirma que a engine de machine-learning "assesses every request" em vez de depender de regras estáticas (Fonte: stechcrunch.com).
Eles também alegam prevenir roubo de conteúdo, tomadas de conta, fraude de anúncios e DDoS "in real time".
Antes de começarmos nossa análise profunda, tenhamos em mente que essa empresa foi financiada com um total de $81 milhões.
Histórico do desafio
Essa página de WAF foi introduzida pela primeira vez por volta de cinco anos atrás durante a "era de ouro dos sneaker games". Quando ela apareceu em sites como Slamjam e Starcow, a única forma de resolvê-la era via reCAPTCHA.

Depois foi substituída por uma implementação do Geetest, e então por um slide captcha estilo Geetest customizado ou um desafio de áudio.
A opção de áudio nunca foi ideal, já que cookies obtidos por esse caminho costumam receber um score de confiança mais baixo, disparando um segundo desafio ou um bloqueio rápido.
Casos de uso
Atualmente, o Datadome ainda é usado por alguns varejistas, pela maioria dos sites de compra dos times de futebol europeus e por alguns sites de gift card de cartão de crédito. Aqui vão alguns exemplos:
- 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/ (e todos os sites sob ele)
E a lista continua…
Ofuscação de Javascript
Dentro da página do iframe, você vai encontrar uma linha longa e minificada contendo o JavaScript real do captcha.

Quando embelezado, esse arquivo revela uma ofuscação bem simples construída sobre técnicas baseadas em arrays e funções.

Ele é bastante longo porque na verdade consiste em oito módulos/arquivos distintos, como mostrado na imagem anexada.


Cada arquivo cumpre um propósito específico e organizado e contém seu próprio conjunto de funções.
Ofuscação de Construções Loop-Switch
Um aspecto digno de nota é a forma peculiar como eles "ofuscam" for-loops e switch statements. É completamente inútil e trivial de reverter, mas eu nunca tinha visto essa técnica antes.

O que ela faz
Em vez de escrever:
case 23
o código constrói uma tabela de lookup bidimensional s e conduz o estado do switch via s[x][y], de modo que todo case fica assim:
case s[263][471]:
Onde a função de geração da tabela se parece com isto:
// 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;
}
**Inspecionando a tabela em tempo de execução
**Se você fizer log de s[yyy][zzz] no console do seu navegador, você não vai obter um número, vai ver um objeto Array (uma linha daquela matriz de lookup).

**Por que é uma ofuscação boba
**Rodando o mesmo gerador de tabela e as funções de mapeamento em um sandbox (por exemplo, a vm do Node), você pode reconstruir a tabela de lookup inteira, derrotando a ofuscação.
// 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
**Limpando tudo
**Uma simples travessia de AST baseada nessa lógica pode substituir cada case s[x][y] pelo literal numérico original, restaurando um switch statement normal.

Rápido e fácil, mas um belo estudo de caso.
Dinamicidade do script
É bem estranho que uma empresa fundada com mais de $80 milhões em financiamento ainda não tenha introduzido um script totalmente dinâmico para cada sessão, como a maioria dos "grandes" provedores de antibot faz atualmente.
Em vez disso, o que eles fizeram foi implementar uma rotação diária dos arquivos, o que significa que o script do iframe muda todo dia em um horário específico.
Essa rotação foi introduzida cerca de um ano atrás; a menos que haja uma atualização ou patch importante, o número da versão permanece o mesmo, com apenas alguns IDs estáticos modificados para identificar o desafio exato sendo resolvido.
Nomes dinâmicos de sinais
Alguns meses atrás, uma mudança importante foi introduzida — primeiro no desafio de Captcha, depois no interstitial também: chaves dinâmicas para os sinais. Todo dia, as chaves no dicionário de sinais mudam para uma string aleatória de seis caracteres. Se você não fizer o match delas corretamente, a resolução será inválida.

Essa tem sido uma técnica esperta para bloquear a maioria dos provedores de API de resolução, mas alguém poderia ter armazenado todos os scripts diários "só por garantia" ao longo dos últimos cinco anos. Eles poderiam então realizar uma busca semântica rápida para identificar sinais renomeados, entradas novas ou removidas, ou outras modificações. :P

Atualmente, esse mecanismo de chaves dinâmicas tirou a maioria dos provedores do jogo ou os forçou a migrar para soluções baseadas em navegador, que na minha opinião são a pior forma de entregar um serviço por conta de:
- Tempos de resolução lentos e uso pesado de recursos
- Lock-in a user agents, navegadores, versões de OS, timestamps ou fingerprints específicos, forçando os clientes a baterem exatamente
- Desenvolvedores frequentemente não fazem ideia do que o antibot verifica, tornando o debug ou a correção de problemas específicos do site difícil
- A necessidade de novos patches de navegador a cada atualização de desafio, resultando em instabilidade para projetos de longo prazo ou críticos
Tudo isso decorre de não ter descoberto uma forma escalável de fazer scraping e identificar os nomes dos sinais diariamente.
Desafio WASM: boring_challenge

Recentemente, um desafio baseado em WASM foi introduzido como parte de um dos sinais. A função principal, chamada boring_challenge, essencialmente força o seu navegador a rodar uma pequena máquina de estado compilada em Rust repetidamente até que ela cuspa um número.
- Faz o decode Base64 de um blob Wasm compacto e o compila sincronamente em um WebAssembly.Module.
- Configura os imports do wasm-bindgen (expande uma tabela de externref com [undefined, null, true, false]).
- Instancia o módulo (que zera alguns globals via seu __wbindgen_start).
- Escolhe uma semente aleatória de 32 bits (entre 10 milhões e 20 milhões) e uma dica de concorrência (a contagem de núcleos da sua CPU).
- Chama boring_challenge(BigInt(seed), BigInt(concurrency)), que salta para um loop massivo e aninhado de manipulação de bits, XORs, shifts, rotates e constantes mágicas — tudo hard-coded em uma máquina de estado de tabela de lookup ofuscada.
- Sai apenas quando essa máquina de estado finalmente chega a um valor terminal, retornando um resultado de 64 bits que o seu script converte de volta para um Number.
Não há nenhum "captcha" real aqui, apenas proof-of-work puro. O servidor pode reinvocar de forma barata a mesma função do seu lado para verificar que você fez o trabalho braçal, mas o seu navegador precisa queimar ciclos de CPU para resolvê-lo.

É trivial de reverter (a gente simplesmente decompila e roda por conta própria). É basicamente mais um "imposto de CPU" do que um fingerprint de verdade, talvez introduzido para criar problemas para navegadores headless (eu suponho?).
Você pode saber mais sobre isso aqui no meu repo open-source
Desafio de hash dinâmico
Depois da introdução do WASM, a "feature" mais recente introduzida é um desafio de hash dinâmico, em que alguns detalhes do navegador, já fornecidos em outros sinais, são montados em uma lista e então hasheados usando um hash de desafio dinâmico específico.
A lista resultante então alimenta uma cadeia dinâmica de operações que se parece com isto:
((((((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
É uma abordagem interessante, mas infelizmente é bem fácil de identificar e extrair, e por depender apenas de cálculos matemáticos, uma simples avaliação já basta.

Verificações de integridade do script
Até hoje, nenhuma verificação de integridade foi adicionada, o que torna fácil aplicar patches para fins de debug.
Entendendo mais sobre as mudanças de versão
Para fins de aprendizado, confira meus módulos de deobfuscation para o DataDome, agora desatualizados e open-source (tanto Interstitial quanto Captcha):
- Datadome-Deobfuscator (Out 2023)
- Datadome-Interstitial-Deobfuscator (Jan 2024)
- Datadome-Captcha-Deobfuscator (Jan 2024)
Resolução de imagens

As imagens retornadas pelo Captcha do Datadome são bem fáceis e rápidas de resolver usando apenas alguns filtros e operações matemáticas. Eu já abri o código de um script Python base ainda funcional para você começar aqui: Datadome-GeeTest-Captcha-Solver

Cálculo dos movimentos
O verdadeiro desafio-chave é garantir que os movimentos fornecidos batam com o que o DataDome espera.
O DataDome analisa os movimentos em duas listas
- _initialCoordsList que captura os movimentos do carregamento da página até o clique no botão de slide
- _coordsList que contém os movimentos do slide
Essas duas listas são então computadas em 31 sinais, com base em curvatura, comprimento, retilinearidade e outras métricas, para sinalizar entradas anormais.

Honestamente, as verificações deles são rígidas e eficazes em parar a maioria dos scripts automatizados baseados em navegador, embora um bom modelo de ML alimentado com bastante dado ainda consiga dar conta :P.
Para ajudar nessa parte do processo, abri o código do Datadome-Movements-Display que visualiza as listas brutas de movimento antes da computação, permitindo comparar os seus padrões gerados com dados genuínos de navegador.
Criptografia dos sinais: ddCaptchaEncodedPayload
Ao adicionar um sinal ao payload final, um método de encoding customizado é usado com base no hash do site.
e(`THcQWT`, i.left)
e(`ds6frg`, i.right)
e(`zzUlTA`, i.up)
e(`uJkDwZ`, i.down);
Visão Geral do Processo de Criptografia

**Inicialização
**O sistema é semeado com um hash, um identificador de cliente (cid) e um salt opcional. Esses parâmetros inicializam um gerador de números pseudoaleatórios (PRNG) que conduz os passos seguintes.
**Construção do Buffer
**Cada sinal (par chave-valor) é ofuscado e submetido a XOR com bytes gerados pelo PRNG. Bytes marcadores ({, }) e separadores (:) também são ofuscados por XOR e anexados para delinear a estrutura.
**XOR c/PRNG 2ª Rodada
**O buffer inteiro passa por uma passagem adicional de XOR usando uma segunda sequência de PRNG derivada do cid e do salt. Essa abordagem de camada dupla garante que o valor final de cada byte dependa de forma intrincada de múltiplos parâmetros dinâmicos.
**Encoding Customizado Estilo Base64
**Após o XOR, o buffer é codificado em uma string usando um algoritmo customizado estilo Base64. Operações adicionais de XOR com um valor de salt decrescente são aplicadas. Um mapeamento único de caracteres garante uma transmissão URL-safe, complicando ainda mais a descriptografia.
O processo de descriptografia simplesmente reverte esses passos assim que você os entende.
Sem me aprofundar demais em detalhes técnicos, para não perder os leitores desinteressados, abri o código de um mergulho profundo na lógica de criptografia e descriptografia aqui, tanto para NodeJS quanto para Python:
Então como o Datadome poderia de fato ser resolvido?
Vamos percorrer o processo passo a passo. Vou assumir que você está usando uma abordagem baseada em requests, porque uma solução baseada em navegador exige patches constantes a cada atualização, alto uso de recursos e oferece pouco controle sobre o que está acontecendo.
- Identifique e construa a URL do desafio a partir da resposta inicial.
- Carregue a página do desafio.
- Faça o parse dos detalhes necessários do desafio carregado.
- Extraia, ordene e interprete as chaves dinâmicas.
- Resolva o desafio de imagem.
- **Monte o seu **dicionário de sinais, os detalhes do navegador, os desafios resolvidos e os movimentos computados.
- Criptografe o payload.
- Envie-o ao endpoint do DataDome.
- Recupere o cookie (se a resolução foi bem-sucedida).
- Recarregue a página alvo.
Conclusão
Em conclusão, isto foi apenas um estudo de caso. Não estou dizendo que o DataDome é um antibot ruim — algumas das técnicas deles são peculiares e interessantes, ainda que fáceis de contornar — mas sim destacando quão fácil e "barato" é o WAF deles, e levantando questões sobre como os milhões que receberam de investidores foram gastos.
Talvez eles devessem remover o artigo How to Bypass DataDome (And Why It's Not That Simple)
Precisa de Soluções de Bypass do DataDome?

Se você precisa de uma solução confiável de bypass do DataDome para o seu projeto, recorra aos especialistas que realmente entendem a tecnologia. Minha empresa, TakionAPI, oferece APIs profissionais de bypass de anti-bot com eficácia comprovada contra o DataDome e outros sistemas de defesa contra bots.
Você pode conferir e rodar o arquivo de exemplo do vídeo aqui.
Chega de se preocupar em entender, reverter e resolver o desafio por conta própria, ou em mantê-lo atualizado todo dia. Uma simples chamada de API faz tudo.
Oferecemos testes gratuitos, implementações de exemplo e assistência de setup para tornar todo o processo fácil e tranquilo. Confira nossa documentação direta aqui, comece o seu teste aqui, ou, para desenvolvimento e suporte customizados, fale conosco no Discord.
Visite TakionAPI.tech para soluções reais e de alta qualidade de bypass de anti-bot; a gente sabe o que está fazendo.
Conecte-se comigo
Se você gostou deste artigo, me siga no GitHub e no Medium para receber notificações sempre que eu postar ou abrir o código de algo.
Fontes
- TechCrunch
- Datadome website
- How to Bypass DataDome (And Why It's Not That Simple) — propaganda do Datadome
- datadome-wasm
- datadome-encryption
- datadome-encryption-python
- Datadome-GeeTest-Captcha-Solver
- Datadome-Movements-Display
- TakionAPI
- TakionAPI DOCS
- Datadome-Deobfuscator (Out 2023 — desatualizado)
- Datadome-Interstitial-Deobfuscator (Jan 2024 — desatualizado)
- Datadome-Captcha-Deobfuscator* (Jan 2024 — desatualizado)*
- Datadome-Interstital-Encryptor (desatualizado)
- How to detect, block and manage DataDome (desatualizado)
Pule a engenharia reversa.
O Takion retorna cookies, headers e tokens novos para cada grande muro antibot. Um POST, sem navegador, primeira chamada em menos de uma hora.