
В 2022 году, когда hCaptcha перестал обслуживать российских пользователей, DDoS-Guard оказались перед выбором: искать новый сервис или создать свою капчу с нуля. Выбрали второе — и не прогадали. В этой статье я расскажу, как из аварийного решения родилась система, которая сегодня защищает миллионы запросов ежедневно. Поделюсь ключевыми выводами наших исследований по устойчивости к распознаванию, раскрою секреты масштабирования без пиковых нагрузок на серверы. Немного затрону тему того, как легко настроить страницу капчи под требуемый дизайн, чтобы переход на проверку оставался незаметным для пользователей. Также объясню, как наша капча превратилась из внутреннего инструмента в самостоятельный продукт.
Зачем вообще капча в системе защиты?
Для начала, давайте вспомним, зачем капча вообще нужна в системе защиты от DDoS. У нас капча — это не первая линия обороны, а скорее «последний рубеж».
Когда наша система защиты решает, что трафик выглядит подозрительно, мы отправляем пользователя на дополнительные проверки. Первая проверка проверяет поведение браузера через JavaScript, то есть происходит автоматически. Если пользователь проходит этот этап, он свободно продолжает работу.
Но если система замечает аномалии, то в таком случае включается капча как окончательная проверка. То есть перед тем, как пустить пользователя дальше, мы хотим убедиться: перед нами живой человек, а не бот.
Когда hCaptcha перестала работать
До 2022 года DDoS-Guard, как и многие, спокойно использовали сторонний сервис hCaptcha. Все работало стабильно, пока в 2022 году некоторые из наших клиентов получили отказ в обслуживании от этого сервиса, из-за чего во время атак небольшая часть посетителей не могла пройти дальше — их ждала пустая страница без капчи.
Нужно было срочно решать проблему, потому что без капчи наша система защиты теряла один из ключевых элементов.
Но вместо того, чтобы просто искать нового вендора, мы задумались: а почему бы не сделать свою капчу? Чтобы больше не зависеть от внешних сервисов, которые могут завтра закрыть доступ или изменить условия обслуживания.
От «черно-белого текста на коленке» до сложных искажений
Начали, как водится, с простого. Первый вариант, который мы запустили «на коленке», был элементарным: черно-белый текст на фиксированном фоне. Просто картинка с текстом, с небольшим поворотом и смещением. Защищало это решение, честно говоря, только от самых тупых ботов, которые не имели доступа к сервисам распознавания текста.

Потом мы пошли дальше и начали экспериментировать. Добавили цвета, но это мало что дало, так как любая OCR обязательно переводит изображение в монохром, да и нейросети легко справляются с цветом. Тогда мы решили пойти по пути усложнения самого текста. Начали использовать «уродские» шрифты, которые вы никогда не использовали бы в нормальном дизайне. Шрифты с разрывами, с имитацией помех. В общем, все то, что мешает роботам читать текст.

Но нейросеть распознает около 80% символов даже с такими шрифтами. Пришлось идти дальше.
Что не сработало (и почему)
Мы пробовали разные подходы, и не все они оказались удачными. Вот что мы тестировали и почему от некоторых вариантов пришлось отказаться:
Фоновый шум. Сначала добавили обычное наложение — просто равномерный фоновый шум, который частично маскирует текст. Потом пошли дальше и попробовали более хитрый вариант: использовали текст только с обводкой, без заливки. Идея была в том, чтобы уменьшить плотность сигнала: роботу сложнее распознать контур, чем сплошной символ.


Но результат показал, что нейросеть, дообученная на 2–3 тысячах таких картинок, быстро адаптировалась и стала распознавать в среднем 4,5 символа из 5. А вот людям стало заметно тяжелее, особенно при плохой освещенности или на маленьких экранах. Вывод очевиден: отправляем в мусорку.
Градиенты. Мы попробовали реализовать сложный визуальный эффект с помощью двух полотен одинакового размера. На каждом из них ставились две точки контрастного цвета, а пространство между ними заполнялось градиентом. Первое полотно использовалось как фон, второе — как маска для букв. В итоге получался графически интересный и довольно красивый результат.
Но на этом плюсы закончились. Проблема оказалась в читаемости для людей. При тестировании выяснилось, что модель распознавала в среднем 2,5 символа из 5, а это значит, что алгоритм не мог надежно разобрать текст. Казалось бы, защита работает отлично. Однако и люди столкнулись с тем же: один из наших коллег вообще не смог пройти такую капчу. Получилось, что мы усложнили картинку настолько, что она перестала быть функциональной. В итоге ни машина, ни человек не могут ее прочитать. Такой вариант бесполезен. Отправляем в мусорку.

Скручивание. Пробовали скручивать текст вокруг определенных точек. Но, как оказалось, чтобы это выглядело нормально, нужно брать достаточно большой радиус, иначе пиксели сливаются и текст становится нечитаемым. Плюс невозможно подобрать один угол скручивания, который подойдет для всех символов, так как некоторые буквы можно крутить сильнее, а другие — нет.
Да и если символы расположены близко друг к другу, при скручивании они начинают превращаться в нечитаемую кашу. В итоге распознаваемость падала до 30-40%, но и для людей капча становилась слишком сложной.

Что сработало: волновые искажения
Оказалось, что наиболее эффективным оказался метод волновых искажений. Суть проста: по вертикали и горизонтали генерируем две гармонические функции (сумма двух синусов), и каждый пиксель смещаем по X и Y в соответствии с этими функциями.
Почему это работает? У машинного обучения большие проблемы с нелинейными искажениями. Нейросети, обученные на прямом тексте, плохо справляются, когда текст искривлен волнами. При этом для человека такой текст остается вполне читаемым, так как мы умеем воспринимать искривленный текст.
В итоге мы получили баланс: нейросети распознавали в среднем только 2,5-3 символа из 5, а люди проходили капчу без проблем. Это была та самая золотая середина, к которой DDoS-Guard и стремились.

Важно: контекст исследований 2022 года и вызовы современных LLM
Наши исследования по эффективности различных капч проводились в 2022 году. Тогда в качестве модели для тестирования устойчивости мы использовали относительно простую CNN — классическую сверточную сеть, которая была стандартом для задач OCR в тот период. На тех данных и при тех подходах волновые искажения показали отличные результаты, что делало автоматизированное прохождение экономически невыгодным.
Однако ситуация меняется. С появлением мощных multimodal LLM, способных анализировать изображения и понимать контекст, старые метрики перестают быть надежными. Такие модели могут определять текст на капче с высокой точностью, даже при сильных искажениях, особенно если запрос сформулирован корректно.
Это значит, что наша текущая капча, хотя и защищена от классических OCR-ботов, может быть уязвима перед новыми технологиями. DDoS-Guard уже начали подготовку к новому раунду исследований, где в качестве атакующего будет выступать именно multimodal LLM. Цель — понять, насколько актуальны наши решения в 2025-2026 году и нужно ли усиливать защиту.
Как DDoS-Guard генерирует и обслуживает капчу в проде: секреты масштабирования
После того как мы определились с алгоритмом генерации, встал вопрос: как это все масштабировать и не упасть под нагрузкой? Ведь если наша система защиты решает показать капчу, значит, трафик уже подозрительный, следовательно, может быть очень большим. И тут мы столкнулись с новой задачей: как сделать так, чтобы сама капча не стала узким местом.
KV-хранилище: зачем нам нужно 16 миллионов капч
Первое, от чего мы отказались сразу — это генерация капчи «на лету». Представьте: пользователь приходит, система решает, что ему нужна капча, генерируется изображение, сохраняется в сессию, пользователь решает и отправляет ответ. Звучит логично, но на практике при высокой нагрузке это превращается в кошмар: каждый запрос к БД, сессии, которые нужно хранить... При DDoS-атаке такая система просто не выживет.
Поэтому DDoS-Guard пошли другим путем: предварительная генерация. У нас есть KV-хранилище, где ключ — это целое число от 0 до 16_777_215 (точнее, до 2^24). Почему именно такой диапазон? Потому что меньше — легко перебрать, больше — избыточно. 24 бита — золотая середина: достаточно случайности для безопасности, но без излишеств.
Каждая запись в этом хранилище содержит изображение капчи и хеш от правильного ответа (секрет).
Как это работает в реальном времени
Когда системе защиты нужно показать капчу пользователю, происходит следующее:

Генерируем случайное число в нашем диапазоне (от 0 до 16_777_215)
Делаем один запрос к KV-хранилищу
Получаем готовую картинку и хеш правильного ответа
И все. Пользователь видит капчу, решает ее, отправляет ответ вместе с этим самым хешем. Нам остается вычислить хеш решения и сравнить с хешем, который пришел в ответе. Если совпадают, то пользователь проходит проверку. Если нет, то показываем капчу заново.
Генерация контента: менеджер и генераторы
Теперь давайте разберем, как мы заполняем эти 16 миллионов записей. Первое, что приходит в голову: «Ну, запустим скрипт, сгенерируем все разом и забудем». Но на практике так не работает: сервера падают, процессы завершаются аварийно, а нам нужно, чтобы база всегда была заполнена. Также нужно уметь постепенно обновлять состояние базы, чтобы не оставлять старые картинки навечно.
Поэтому мы разделили процесс на два компонента: менеджера и генератора

Менеджер
Это центральный координатор системы. Он знает состояние всей базы: какие диапазоны (или батчи) уже заполнены, какие — нет. Также он отслеживает, сколько генераторов сейчас работает и как быстро они справляются с задачами.
Генераторы
Генератор — это специальный воркер, который можно запустить в нескольких экземплярах, то есть они горизонтально масштабируемые. Каждый генератор работает по простому алгоритму:
Обращается к менеджеру: «Есть ли новое задание?»
Получает батч, например, диапазон от 110 до 230
Для каждого ключа в этом диапазоне:
Генерирует случайный ответ (например, a7k9m2)
Вычисляет его хеш
Создает картинку
-
Сохраняет все в KV-хранилище
4. Сообщает менеджеру: «Задание выполнено!»
Если генератор упал или не успел выполнить задание в отведенное время, менеджер просто отдаст этот батч другому генератору. Да, будет небольшое дублирование, ведь некоторые капчи перегенерируются, но это нормально. Лучше перегенерировать 1% данных, чем иметь «дыры» в пространстве ключей.
Два режима работы: активный и пассивный
Теперь поговорим о том, как мы поддерживаем актуальность нашей базы капч. Это не статичная система, так как со временем картинки нужно обновлять, иначе злоумышленники могут собрать базу и использовать ее против нас.
У нас два режима работы генераторов:

Активный режим
Этот режим запускается в двух случаях: когда мы только начинаем заполнять базу (первичная инициализация) или когда мы меняем алгоритм генерации (например, обновляем параметры волновых искажений).
В таком режиме:
Генераторы получают большие батчи — по несколько тысяч ключей за раз
Задания выдаются часто, как только предыдущие выполнены
Цель — заполнить всю базу как можно быстрее, обычно за 3–5 часов
Это нужно, когда мы хотим быстро обновить все пространство ключей. Например, после изменений в алгоритме генерации мы переводим систему в активный режим, и за пару часов все старые капчи заменяются на новые.
Пассивный режим
Этот режим включается автоматически, когда база полностью заполнена и нет необходимости в срочном обновлении.
В таком режиме:
Генераторы получают небольшие батчи — по сотне ключей за раз.
Задания выдаются с интервалами, чтобы не создавать лишнюю нагрузку.
Цель — постепенно обновлять старые картинки, чтобы не было «застоя». Обычно база полностью обновляется за неделю.
Как выглядит страница прохождения капчи
Сама страничка выглядит просто и, возможно, некоторым знакомо. Заранее просим прощение, если посчитали вас подозрительным :)


Если стандартный вид капчи не вписывается в дизайн вашего сайта, то это не проблема. Через личный кабинет DDoS-Guard, в разделе кастомных страниц, вы можете загрузить свою верстку и стили, чтобы страница капчи выглядела как естественная часть вашего сервиса.
Заходим в раздел «Кастомные страницы», нажимаем «Изменить» в блоке CAPTCHA.

Далее нас встречает модальное окно, где мы можем загрузить html-файл, который соответствует требованиям, указанным в инструкции.

После проверок на валидность загруженного файла отобразится предварительный просмотр нашей страницы. Убедившись, что все выглядит как надо, сохраняем настройки.

И вуаля, теперь для подозрительного запроса нас просят пройти капчу, но уже с нашей кастомизацией.

Что было дальше: как наша капча стала самостоятельным продуктом
На этом можно было бы закончить историю, так как у нас появилась своя капча, она работает, защищает клиентов, и мы не зависим от внешних сервисов.
Но наша капча была частью системы DDoS-защиты. Это значит, что она запускалась только тогда, когда сама система защиты решала, что трафик подозрительный. Такой подход работал для основной задачи — фильтрации ботнетов и автоматических атак.
Однако в 2025 году к нам начали приходить клиенты с одной и той же просьбой: «А можно использовать вашу капчу не только в рамках защиты, но и в наших формах? Например, при регистрации, входе или отправке заявки?» Наша капча им нравилась, но использовать ее вне нашей системы было невозможно. Она была «встроенной», а не «встраиваемой».
В начале 2025 года DDoS-Guard запустили исследование: можно ли сделать из нашей капчи отдельный модуль, который будет работать как standalone-сервис, подобно Google reCAPTCHA или hCaptcha? Задача стояла четко: отвязать капчу от системы защиты и превратить ее в универсальный инструмент для валидации пользователей.
Мы взяли за основу знакомый всем паттерн:
Клиент подключает скрипт.
Скрипт показывает капчу.
Пользователь проходит проверку.
Сервер клиента получает токен и проверяет его через API.
Это позволило нам сохранить привычный UX для тех, кто уже использовал сторонние решения. Переход с Google на нашу капчу стал максимально гладким — минимум изменений в коде, понятная документация, простая интеграция.
Что будет дальше: кастомизация и контроль
Сейчас мы активно развиваем модуль, и в ближайшие версии планируем добавить:
Настройку сложности. Не все формы требуют одинаковой защиты. Для входа можно сделать капчу проще, для регистрации — строже. Мы дадим клиентам, например, возможность выбирать уровень искажений и длину ответа.
Кастомизацию формы. Если стандартный вид капчи не вписывается в дизайн сайта, то это не проблема. Через личный кабинет, в разделе кастомных страниц, вы сможете загрузить свою верстку и стили, чтобы капча выглядела как естественная часть вашего сервиса.
Поддержку языков. Сейчас капча использует латиницу и цифры. В будущем мы добавим возможность выбирать язык испытания, например, русский, если это важно для вашей аудитории.
Спасибо, что дочитали до конца. Если у вас есть опыт создания или обхода капч, делитесь в комментариях — особенно интересно узнать, сталкивались ли вы с теми же проблемами, что и мы. А если заметите, что наша капча стала слишком простой для ботов — дайте знать, будем улучшать.
Комментарии (2)

aax
19.11.2025 05:55Пользователь видит капчу, решает ее
Я как пользователь ухожу(за редчайшим исключением) от сервисов с такой политикой.
Замечу, что более продвинутые сервисы(например Cloudflare) умеют отличать человека от бота просто по манере клика по чекбоксу "Я человек".
tuxi
Мы для форм логина регистрации и тп капчу не используем. Генерим динамические имена параметров для форм, кладем их в сессию на сервере. Так же ставим в формах honeypot-ы для ботов без поддержки js.
Попавшийся бот маркируется как серый, дальше при последующем запросе анализируется соотношение кол-ва запросов к защищаемой части ресурса к общему кол-ву запросов к остальным частям ресурса, для реального посетителя этот параметр плюс/минус всегда одинаковый. Если бот не проходит проверку, он помечается как "черный" с соответствующими последствиями для него. "Белые" боты исключены из проверок.