Разбираем WAF Datadome Captcha
Давайте шаг за шагом разберём последнюю версию WAF Datadome Captcha/Geetest, чтобы понять, как получить доступ к защищённым им сайтам.

Давайте шаг за шагом разберём последнюю версию WAF Datadome Captcha/Geetest, чтобы понять, как получить доступ к защищённым им сайтам.

Вас заблокировали!
Ниже приведён ответ, который вы увидите, когда запрос блокируется WAF Datadome Captcha при парсинге защищённого сайта:
<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>
Используя возвращённый словарь dm, подключённый JavaScript генерирует фактический URL проверки и внедряет его на страницу в виде iframe. После встраивания iframe вы либо увидите captcha, которую нужно пройти, либо сообщение «You have been blocked». Это означает одно из двух:
- Ваш IP-адрес помечен как подозрительный.
- Поток ваших запросов или заголовки отклоняются от нормального поведения браузера, что запускает обнаружение ботов Datadome.
Чтобы этого избежать, всегда сначала проверяйте свои прокси в реальном браузере — убедитесь, что они позволяют пройти проверку и перемещаться по сайту, — и точно воспроизводите в своём скрипте ту же последовательность запросов и заголовки, что и в этой браузерной сессии.
Запрос
После прохождения проверки отправляется следующий GET-запрос:

При успешном прохождении возвращается cookie Datadome. Большинство значений в этом запросе просто извлекаются со страницы. Единственный важный элемент — это ddCaptchaEncodedPayload, который мы рассмотрим и разберём далее в этой статье. По сути, это словарь, содержащий различные данные, называемые сигналами (signals).

Как Datadome себя позиционирует
«Дело не только в том, человек это или бот, — дело в намерении. Наш ИИ определяет вредоносное или легитимное намерение в реальном времени, будь то бот или человек».
Компания называет себя «лидером в защите от киберфрода» и хвастается блокировкой «миллиардов» атак, обрабатывая более триллиона сигналов в день на 25+ точках присутствия по всему миру. Она делает упор на своём высокоскоростном ИИ-движке: например, в релизе от апреля 2025 года утверждается, что он способен «идентифицировать, классифицировать, адаптироваться и реагировать на трафик менее чем за 2 миллисекунды».
«Большинство инструментов обнаружения ботов полагаются на статическую, основанную на правилах логику, которая не поспевает за новыми угрозами. ИИ-движок DataDome устроен иначе. Он непрерывно обучается и адаптируется в реальном времени, используя как контролируемые, так и неконтролируемые модели, …»
Маркетинговые материалы часто противопоставляют это устаревшим средствам защиты; в блогах утверждается, что WAF на основе правил «архаичны» и «не могут тягаться с продвинутыми ботами».
В одном из блогов DataDome даже называет себя «единственным решением для защиты от ботов и онлайн-фрода, поставляемым как сервис».
Их CEO утверждает, что движок машинного обучения «оценивает каждый запрос» вместо того, чтобы полагаться на статические правила (Источник: stechcrunch.com).
Они также заявляют, что предотвращают кражу контента, захват учётных записей, рекламный фрод и DDoS «в реальном времени».
Прежде чем приступить к нашему глубокому анализу, будем помнить, что эта компания привлекла в общей сложности $81 миллион финансирования.
История проверки
Эта страница WAF впервые появилась около пяти лет назад, во времена «золотой эры sneaker-игр». Когда она появилась на таких сайтах, как Slamjam и Starcow, единственным способом её пройти был reCAPTCHA.

Позже её заменили реализацией на Geetest, а затем — кастомной слайдер-captcha в стиле Geetest или аудио-проверкой.
Аудио-вариант никогда не был идеальным, поскольку полученные таким образом cookie часто получают более низкий рейтинг доверия, что запускает повторную проверку или быструю блокировку.
Сценарии использования
Сейчас Datadome по-прежнему применяется некоторыми ритейлерами, большинством сайтов по продаже билетов европейских футбольных клубов и рядом сайтов с подарочными картами (в том числе кредитными). Вот несколько примеров:
- 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/ (и все сайты под ним)
И этот список можно продолжать…
Обфускация JavaScript
Внутри страницы iframe вы найдёте длинную минифицированную строку, содержащую фактический JavaScript captcha.

После форматирования этот файл раскрывает довольно простую обфускацию, построенную на приёмах с массивами и функциями.

Он довольно объёмный, потому что на самом деле состоит из восьми отдельных модулей/файлов, как показано на прикреплённом изображении.


Каждый файл выполняет свою конкретную, чётко определённую задачу и содержит собственный набор функций.
Обфускация конструкций Loop-Switch
Один примечательный момент — необычный способ, которым они «обфусцируют» циклы for и операторы switch. Это совершенно бессмысленно и элементарно реверсится, но раньше я такого приёма не встречал.

Что он делает
Вместо того чтобы писать:
case 23
код строит двумерную таблицу поиска s и управляет состоянием switch через s[x][y], так что каждый case выглядит так:
case s[263][471]:
Где функция генерации таблицы выглядит следующим образом:
// 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;
}
**Инспектирование таблицы во время выполнения
**Если вы залогируете s[yyy][zzz] в консоли браузера, вы получите не число, а объект Array (одну строку этой матрицы поиска).

**Почему это глупая обфускация
**Запустив тот же генератор таблицы и функции сопоставления в песочнице (например, в Node vm), вы можете восстановить всю таблицу поиска и тем самым победить обфускацию.
// 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
**Наводим порядок
**Простой обход AST на основе этой логики может заменить каждый case s[x][y] исходным числовым литералом, восстанавливая нормальный оператор switch.

Быстро и легко, но хороший разбор.
Динамичность скрипта
Довольно странно, что компания, привлёкшая более $80 миллионов финансирования, до сих пор не внедрила полностью динамический скрипт для каждой сессии, как это сейчас делают большинство «крупных» антибот-провайдеров.
Вместо этого они сделали ежедневную ротацию файлов: скрипт iframe меняется каждый день в определённое время.
Эта ротация была введена около года назад; если не было крупного обновления или патча, номер версии остаётся прежним — меняется лишь пара статичных ID, идентифицирующих конкретную проверку, которую нужно пройти.
Динамические имена сигналов
Несколько месяцев назад было внесено крупное изменение — сначала в проверку Captcha, а затем и в interstitial: динамические ключи для сигналов. Каждый день ключи в словаре сигналов меняются на случайную строку из шести символов. Если вы не сопоставите их правильно, решение будет недействительным.

Это был хитрый приём, чтобы заблокировать большинство API-провайдеров решения captcha, но кто-то мог за последние пять лет сохранить все ежедневные скрипты «на всякий случай». После этого он мог бы выполнить быстрый семантический поиск, чтобы выявить переименованные сигналы, новые или удалённые записи и прочие изменения. :P

Сейчас этот механизм динамических ключей выбил из игры большинство провайдеров или вынудил их перейти на браузерные решения, которые, на мой взгляд, являются худшим способом предоставления сервиса из-за:
- Медленного времени решения и высокого потребления ресурсов
- Привязки к конкретным user agent, браузерам, версиям ОС, временным меткам или фингерпринтам, что вынуждает клиентов точно им соответствовать
- Того, что разработчики часто понятия не имеют, что именно проверяет антибот, из-за чего отладка или исправление проблем на конкретных сайтах затруднены
- Необходимости новых патчей браузера при каждом обновлении проверки, что приводит к нестабильности долгосрочных или критически важных проектов
Всё это происходит из-за того, что не найден масштабируемый способ парсить и определять имена сигналов на ежедневной основе.
WASM-проверка: boring_challenge

Недавно в составе одного из сигналов была введена проверка на основе WASM. Основная функция, называемая boring_challenge, по сути заставляет ваш браузер многократно прогонять крошечный конечный автомат, скомпилированный из Rust, пока он не выдаст число.
- Декодирует из Base64 компактный Wasm-блоб и синхронно компилирует его в WebAssembly.Module.
- Настраивает импорты wasm-bindgen (расширяет таблицу externref значениями [undefined, null, true, false]).
- Инстанцирует модуль (который обнуляет некоторые глобальные переменные через свой __wbindgen_start).
- Выбирает случайный 32-битный seed (от 10 до 20 миллионов) и подсказку по параллелизму (число ядер вашего CPU).
- Вызывает boring_challenge(BigInt(seed), BigInt(concurrency)), который уходит в огромный вложенный цикл с манипуляциями над битами, XOR, сдвигами, вращениями и магическими константами — всё это зашито в обфусцированный конечный автомат на основе таблицы поиска.
- Выходит только тогда, когда этот конечный автомат наконец достигает терминального значения, возвращая 64-битный результат, который ваш скрипт преобразует обратно в Number.
Здесь нет никакой настоящей «captcha» — только чистый proof-of-work. Сервер может дёшево повторно вызвать ту же функцию у себя, чтобы проверить, что вы проделали работу, но вашему браузеру приходится жечь такты CPU, чтобы её решить.

Реверсить это элементарно (мы просто декомпилируем и запускаем сами). По сути, это скорее «налог на CPU», чем настоящий фингерпринт, — возможно, введённый, чтобы создавать проблемы headless-браузерам (я так предполагаю?).
Подробнее об этом можно узнать в моём open-source репозитории
Динамическая hash-проверка
После введения WASM последней «фичей» стала динамическая hash-проверка, при которой некоторые данные браузера, уже переданные в других сигналах, собираются в список, а затем хешируются с помощью специального динамического challenge hash.
Получившийся список затем подаётся в динамическую цепочку операций, которая выглядит так:
((((((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
Интересный подход, но, к сожалению, его довольно легко распознать и извлечь, а поскольку он опирается исключительно на математические вычисления, достаточно простого вычисления выражения.

Проверки целостности скрипта
По сей день никаких проверок целостности не добавлено, что упрощает патчинг для целей отладки.
Подробнее об изменениях версий
В учебных целях загляните в мои уже устаревшие open-source модули деобфускации для DataDome (как Interstitial, так и Captcha):
- Datadome-Deobfuscator (Oct 2023)
- Datadome-Interstitial-Deobfuscator (Jan 2024)
- Datadome-Captcha-Deobfuscator (Jan 2024)
Решение изображений

Изображения, возвращаемые Datadome Captcha, решаются довольно легко и быстро с помощью всего пары фильтров и математических операций. Я уже выложил в open source по-прежнему рабочий базовый Python-скрипт для старта: Datadome-GeeTest-Captcha-Solver

Вычисление движений
Настоящая ключевая проблема — обеспечить, чтобы предоставленные движения соответствовали тому, что ожидает DataDome.
DataDome анализирует движения в двух списках
- _initialCoordsList, который фиксирует движения от загрузки страницы до клика по кнопке слайдера
- _coordsList, который содержит движения слайдера
Эти два списка затем вычисляются в 31 сигнал на основе кривизны, длины, прямолинейности и других метрик, чтобы выявлять аномальный ввод.

Честно говоря, их проверки строги и эффективны в остановке большинства браузерных автоматизированных скриптов, хотя хорошая ML-модель, накормленная достаточным объёмом данных, всё же способна справиться :P.
Чтобы помочь с этой частью процесса, я выложил в open source Datadome-Movements-Display, который визуализирует сырые списки движений до вычисления, позволяя сравнить ваши сгенерированные паттерны с настоящими данными браузера.
Шифрование сигналов: ddCaptchaEncodedPayload
При добавлении сигнала в итоговый payload используется кастомный метод кодирования на основе хеша сайта.
e(`THcQWT`, i.left)
e(`ds6frg`, i.right)
e(`zzUlTA`, i.up)
e(`uJkDwZ`, i.down);
Обзор процесса шифрования

**Инициализация
**Система инициализируется хешем, идентификатором клиента (cid) и опциональной солью. Эти параметры инициализируют генератор псевдослучайных чисел (PRNG), который управляет последующими шагами.
**Построение буфера
**Каждый сигнал (пара ключ-значение) обфусцируется и подвергается XOR с байтами, сгенерированными PRNG. Маркерные байты ({, }) и разделители (:) также XOR-обфусцируются и добавляются для обозначения структуры.
**XOR с PRNG, второй раунд
**Весь буфер проходит дополнительный проход XOR с использованием второй PRNG-последовательности, полученной из cid и соли. Такой двухслойный подход гарантирует, что итоговое значение каждого байта сложным образом зависит от нескольких динамических параметров.
**Кастомное Base64-подобное кодирование
**После XOR буфер кодируется в строку с помощью кастомного Base64-подобного алгоритма. Применяются дополнительные операции XOR с уменьшающимся значением соли. Уникальное сопоставление символов гарантирует URL-безопасную передачу, дополнительно усложняя расшифровку.
Процесс расшифровки просто обращает эти шаги, как только вы их понимаете.
Не углубляясь чрезмерно в технические детали, чтобы не потерять незаинтересованных читателей, я выложил в open source подробный разбор логики шифрования и расшифровки — как для NodeJS, так и для Python:
Так как же на самом деле можно пройти Datadome?
Давайте разберём процесс шаг за шагом. Я исхожу из того, что вы используете подход на основе requests, поскольку браузерное решение требует постоянного патчинга под каждое обновление, потребляет много ресурсов и даёт мало контроля над происходящим.
- Определите и постройте URL проверки из первоначального ответа.
- Загрузите страницу проверки.
- Разберите необходимые данные из загруженной проверки.
- Извлеките, отсортируйте и интерпретируйте динамические ключи.
- Решите проверку с изображением.
- **Соберите свой **словарь сигналов, данные браузера, решённые проверки и вычисленные движения.
- Зашифруйте payload.
- Отправьте его на эндпоинт DataDome.
- Получите cookie (если решение было успешным).
- Перезагрузите целевую страницу.
Заключение
В итоге, это был всего лишь разбор одного случая. Я не утверждаю, что DataDome — плохой антибот: некоторые их приёмы уникальны и интересны, даже если их легко обойти. Скорее я хочу показать, насколько простой и «дешёвый» их WAF, и поднять вопрос о том, на что были потрачены миллионы, полученные от инвесторов.
Пожалуй, им стоило бы убрать статью How to Bypass DataDome (And Why It’s Not That Simple)
Нужны решения для обхода DataDome?

Если вам нужно надёжное решение для обхода DataDome в вашем проекте, обратитесь к экспертам, которые действительно понимают эту технологию. Моя компания TakionAPI предлагает профессиональные API для обхода антибот-защиты с проверенной эффективностью против DataDome и других систем защиты от ботов.
Вы можете посмотреть и запустить пример файла из видео здесь.
Больше никаких забот о том, чтобы самому разбираться, реверсить и решать проверку, или ежедневно поддерживать всё в актуальном состоянии. Один простой вызов API делает всё за вас.
Мы предоставляем бесплатный доступ, примеры реализаций и помощь в настройке, чтобы весь процесс был простым и гладким. Ознакомьтесь с нашей понятной документацией здесь, начните бесплатный доступ здесь, а для кастомной разработки и поддержки напишите нам в Discord.
Заходите на TakionAPI.tech за настоящими, качественными решениями для обхода антибот-защиты — мы знаем, что делаем.
Свяжитесь со мной
Если вам понравилась эта статья, подпишитесь на меня на GitHub и Medium, чтобы получать уведомления каждый раз, когда я что-то публикую или выкладываю в open source.
Источники
- TechCrunch
- Datadome website
- How to Bypass DataDome (And Why It’s Not That Simple) — пропаганда Datadome
- datadome-wasm
- datadome-encryption
- datadome-encryption-python
- Datadome-GeeTest-Captcha-Solver
- Datadome-Movements-Display
- TakionAPI
- TakionAPI DOCS
- Datadome-Deobfuscator (Oct 2023 — устарело)
- Datadome-Interstitial-Deobfuscator (Jan 2024 — устарело)
- Datadome-Captcha-Deobfuscator* (Jan 2024 — устарело)*
- Datadome-Interstital-Encryptor (устарело)
- How to detect, block and manage DataDome (устарело)
Пропустите реверс-инжиниринг.
Takion возвращает свежие cookie, заголовки и токены для любой крупной антибот-стены. Один POST, без браузера, первый вызов в течение часа.