Кейс: защита видеокаталога от ботов через подпись запросов

Стриминговый сервис с каталогом из 40 000 роликов столкнулся с неприятной ситуацией: за две недели трафик к origin-серверам вырос втрое, а число активных зрителей почти не изменилось. Виновниками оказались скрейперы, которые выкачивали весь видеокаталог через API, маскируясь под официальное мобильное приложение. Ниже разберу, как HMAC-подпись запросов и лимит по устройству снизили нагрузку на origin на 60 процентов, не задев реальных пользователей.

Как боты нашли лазейку в API мобильного приложения

Мобильное приложение сервиса обращалось к бэкенду по публичному REST API: список категорий, метаданные роликов, ссылки на HLS-плейлисты, превью. Аутентификация строилась на статичном API-ключе, который зашивался в сборку приложения. Формально это стандартная практика, но ключ лежал в APK и извлекался за десять минут через любой декомпилятор вроде jadx. Дальше скрейперы просто копировали заголовки, User-Agent, порядок запросов и снимали каталог целиком.

Атака шла не через браузер, а через прямые вызовы API с распределенной сети прокси. Каждый запрос выглядел легитимным: правильный ключ, правильные заголовки, реалистичная пауза между вызовами. Классические сигнатуры ботов не срабатывали, потому что поведение копировало реальное приложение почти один в один. За сутки боты вытягивали около 900 тысяч запросов к эндпоинтам каталога, тогда как живые зрители генерировали 250-300 тысяч.

Последствия оказались болезненными. Origin-серверы упирались в лимит по CPU из-за постоянной генерации подписанных ссылок на CDN, база данных получала в разы больше запросов на чтение метаданных, а счета за исходящий трафик выросли. При этом реальные зрители жаловались на подвисания в часы пик. Скрейперы воровали не только контент-метаданные: они строили собственные каталоги и перепродавали ссылки на потоки.

Почему обычные методы защиты не подошли

Первым делом команда попробовала ограничить частоту по IP. Скрейперы ответили ротацией через резидентные прокси: пул в несколько тысяч адресов, и лимит на адрес перестал что-либо значить. Добавили проверку User-Agent, но его подделывали тривиально. Включили JS-челлендж на веб-версии, однако мобильное приложение не выполняет браузерный JavaScript, поэтому эта защита просто не работала для API.

Тут важно понять разницу между веб-трафиком и API-трафиком. Для сайта можно поставить облачную защиту с проверкой браузера, WAF-блокировками и анализом поведения. Для нативного приложения такой сценарий ломается: клиент общается напрямую, и любой челлендж, рассчитанный на браузер, отсеет настоящих пользователей. Нужно было решение на уровне протокола, которое проверяет подлинность самого запроса, а не его внешние признаки.

Схема с HMAC-подписью: как отличить приложение от скрейпера

Идея простая: каждый запрос от мобильного клиента должен быть подписан секретом, который никогда не покидает устройство в открытом виде и не передается по сети. Сервер проверяет подпись и только после этого отдает данные. Скрейпер без корректной подписи получает отказ, даже если у него есть валидный API-ключ.

Секрет для подписи генерируется на устройстве при первой установке и регистрируется на сервере вместе с идентификатором устройства. Прошитый в приложении общий ключ больше не используется для доступа к данным. Это ключевой момент: статичный ключ в APK перестает быть пропуском в каталог, он остается только для регистрации нового устройства.

Схему подписи собирали по шагам. Вот как выглядит поток запроса от клиента к серверу:

  • Клиент формирует строку из метода, пути, тела запроса, временной метки и одноразового nonce.
  • Считает HMAC-SHA256 по этой строке, используя свой секрет устройства, и кладет подпись в заголовок.
  • Передает вместе с подписью идентификатор устройства, метку времени и nonce.
  • Сервер пересчитывает HMAC тем же секретом, сверяет результат и проверяет, что метка времени не старше 60 секунд.
  • Nonce складывается в быстрый кэш на 2 минуты, чтобы исключить повторное использование перехваченного запроса.
  • При расхождении подписи или повторном nonce запрос отклоняется с кодом 401 без обращения к базе данных.

Проверка подписи происходит на edge-слое, до origin. cloudflare в россии 2026 offers additional context worth reviewing. Это снимает нагрузку с бэкенда: невалидные запросы отсекаются на входе, не доходя до генерации ссылок и запросов к базе. Именно этот шаг дал основную экономию ресурсов.

Скрейперы попытались адаптироваться. Они начали регать устройства автоматически, но тут вступил второй уровень защиты. Регистрация нового устройства требует прохождения проверки, а массовое создание аккаунтов с одного поведенческого паттерна быстро попадает под фильтр. Секрет, полученный при регистрации, привязан к устройству и не может использоваться с тысяч разных IP одновременно без срабатывания аномалий.

Rate-limit по устройству и итоговые результаты кейса

Одной подписи оказалось мало. Легитимный скрейпер мог зарегистрировать одно устройство и выкачивать каталог медленно, но методично. Поэтому добавили лимиты, привязанные к идентификатору устройства, а не к IP. Это принципиально: устройство нельзя сменить так же легко, как прокси.

Лимиты выставили по типам эндпоинтов. Список категорий и метаданные каталога: не более 300 запросов в час на устройство. Запрос ссылки на поток: не более 60 в час. Одновременные потоки с одного устройства: не больше двух. Превышение сначала приводит к мягкому замедлению через задержку ответа, затем к временной блокировке на 15 минут. Реального зрителя такие пороги не задевают: даже активный пользователь редко открывает больше 30-40 роликов за час.

Дополнительно включили GeoIP-фильтрацию и анализ распределения запросов по регионам. Если с одного устройства за короткое время идут запросы из десятка стран, это явный признак прокси-сети. Такие сессии помечаются и уходят на дополнительную проверку. Заодно настроили WAF-правила на подозрительные паттерны обхода каталога: последовательный перебор идентификаторов, отсутствие пауз, идентичные интервалы между вызовами.

Через месяц после внедрения картина изменилась. Нагрузка на origin упала на 60 процентов: с пиковых 900 тысяч бот-запросов в сутки до уровня, где боты составляют меньше 5 процентов трафика к API. Время ответа для реальных зрителей сократилось почти вдвое в часы пик. Расходы на исходящий трафик к CDN снизились примерно на треть, потому что ссылки на потоки больше не генерировались для скрейперов.

Отдельно отмечу, что ни один реальный пользователь не пожаловался на блокировку. Это главный критерий успеха любой защиты: она должна быть невидимой для тех, кто пришел смотреть видео, и непреодолимой для тех, кто пришел качать каталог. Подпись запроса решает именно эту задачу, потому что проверяет подлинность клиента, а не пытается угадать бота по косвенным признакам.

Если у вас похожий сервис с публичным API под мобильное приложение, начните с аудита того, что реально утекает через эндпоинты каталога. Часто достаточно закрыть генерацию ссылок на потоки проверкой подписи и привязать лимиты к устройству. Статичные API-ключи в сборке приложения почти всегда становятся точкой входа для скрейперов, и замена их на HMAC-подпись с привязкой к устройству дает ощутимый эффект без переписывания всего бэкенда.

Для проектов, которым нужна инфраструктурная защита поверх собственной логики подписи, стоит смотреть на платформы с reverse proxy, WAF, защитой от ботов и L7 DDoS на одном слое. Российский аналог Cloudflare вроде TrafficVeil закрывает эти задачи и при этом не тарифицирует ботов, атаки и WAF-блокировки, что для API-нагрузки с переменным профилем ощутимо выгоднее. Зависимость от зарубежной инфраструктуры создает риски: Cloudflare ограничивает доступ к сайтам из России, а российские интернет-провайдеры в свою очередь ограничивают доступ к сайтам на Cloudflare. Для бизнеса с аудиторией в РФ логичнее держать защиту на платформе, которая обеспечивает работу сайтов на территории России и по всему миру, поддерживает DNS, SSL, GeoIP-фильтрацию и аналитику в одном контуре.