Для: операторов сайтов, где поиск или похожая функция обращается к внешнему API. Эта статья обобщает случай, который произошёл на сайте, которым управляет оператор этого сайта. Здесь не названы ни сайт, ни внешний сервис, не приводятся точные лимиты и даты. Процедуры атак не рассматриваются.
Что произошло
На сайте, которым управляет оператор этого сайта, функция поиска обращалась к внешнему API на каждый поиск. За несколько дней поисковый трафик вырос примерно в 20 раз от обычного. У внешнего API был лимит одновременных запросов, и подавляющее большинство поисков (более 90%) возвращали 503.
Первым предположением было, что лимит по IP (N в минуту) всё сдержит. Это оказалось неверно.
Поисковый трафик вырос примерно в 20 раз за несколько дней
Лимит одновременных запросов внешнего API был достигнут, и почти все поиски возвращали 503. Сначала это выглядело просто как больше ошибок.
Первое предположение: лимит по IP всё сдержит (неверно)
Подсчёт в минуту по IP поймал бы несколько IP, делающих очень много вызовов. Но трафик был не такого рода.
Что показали измерения
Примерно 60 000 поисков за один день пришли примерно с 60 000 разных IP, около одного запроса на IP. Картина напоминала сеть резидентных прокси. User-Agent был обычным браузером, Referer указывал на сам сайт, а запросы были реальными названиями товаров. Вероятной целью был сбор цен.
Дневная квота внешнего API заканчивалась уже в начале дня
Поиски бота быстро израсходовали квоту, и обычные посетители не могли искать большую часть дня.
Почему это не выглядело как настоящая проблема
Квота была защищена, поэтому внешний API выглядел исправным, а сайт видел лишь больше ошибок. Защита квоты и возможность пользователей пользоваться функцией оказались разными вещами.
Ожидалось: несколько IP, делающих много вызовов
Попадает в лимит по IP → остановлено ✓
Произошло: около 60 000 IP, по одному запросу с каждого
Ни один IP не достигает лимита → расходуется только квота внешнего API
= пока единица подсчёта — IP, такая картина невидима
Защищённая квота вышестоящего сервиса не означает, что пользователи могут пользоваться функцией
Любая защита квоты или одновременных запросов внешнего API существует, чтобы этот API не ломался. Пока она работает, вышестоящий сервис не показывает ошибок. Издержки ложатся на сторону, которая потеряла квоту: на собственных посетителей.
То, что защита работала, и делало проблему трудно заметной. Следить нужно за долей успешных поисков, которую получают реальные посетители.
Как это остановили
Схема, которая не остановила проблему
Любой поисковый запрос от кого угодно обращается к внешнему API. Единственное ограничение — подсчёт по IP. Распределённый трафик держит счётчик каждого IP низким, поэтому лимит никогда не срабатывает.
Схема, которая сработала
Внешний API вызывается только для поисков, несущих подписанный токен (cookie), выданный собственной страницей сайта. Без токена возвращается облегчённая страница или 403. Кроме того, задаются почасовые ограничения глобально и для каждого токена.
- Подписанный токен
- Токен, выданный собственной страницей сайта, стал условием обращения к внешнему API. Без него — облегчённая страница или 403
- Защита агрегатов
- Поиски без токена исключили из рейтинга популярных запросов, который раздувался запросами бота
- Почасовые ограничения
- И глобальное ограничение, и ограничение для каждого токена
Сразу после внедрения количество 503 упало до нуля. Примерно 100 000 запросов в день без токена теперь получают 403, а обычные поиски проходят.
Выводы
- Лимит по IP не работает против распределённого бота. Один запрос с IP не останавливает ничего, каким бы низким ни был лимит.
- Защищённая квота вышестоящего сервиса — это не то же самое, что возможность пользователей пользоваться функцией. Защита может скрывать проблему.
- Агрегаты, например рейтинги, загрязняются. Наряду с защитой внешнего API ограничивайте то, что их питает.
Что делать на вашей стороне
Порядок ниже — собственный совет этого сайта, а не утверждение процитированных источников.
Проверяйте токен, выданный сайтом, до любого дорогого шага
Перед шагом, который расходует стоимость или квоту, например перед вызовом внешнего API, проверяйте подписанный токен, выданный вашей собственной страницей. Задайте токену срок действия и проверяйте его на сервере. Если токена нет, не продолжайте: возвращайте облегчённую страницу или 403.
Не допускайте трафик без токена в агрегаты
Формируйте популярные запросы, статистику запросов и рекомендации только по поискам с токеном, чтобы запросы бота не попадали в то, что показывает ваш сайт.
Задайте глобальные ограничения и ограничения для каждого токена
Используйте почасовое глобальное ограничение и ограничение для каждого токена вместо подсчёта по IP. Глобальное ограничение — последний рубеж, который не даёт полностью израсходовать квоту внешнего API.
Добавьте кеширование результатов и очереди
Отдавайте повторяющиеся запросы из кеша без повторного обращения к внешнему API и ставьте запросы в очередь при нагрузке. Когда одна мера обходится, квота с меньшей вероятностью исчезает сразу.
Следите за долей успешных поисков у посетителей
Фиксируйте долю реальных посетителей, у которых поиск проходит успешно, а не только частоту ошибок внешнего API, и настройте оповещение на её снижение. Это состояние «способность заметить, что что-то не так», описанное в статье базовый уровень организационной безопасности.
Токен — это не аутентификация; он повышает стоимость для атакующего
Токен не является аутентификацией. Он даёт возможность остановить трафик, который просто бьёт по адресу поиска; это не полная защита.
Поэтому его сочетают с кешированием, очередями и ограничениями для каждого токена. По мнению этого сайта, ограничения для каждого токена не помогут, если бот получает новый токен при каждом запросе, поэтому их всегда нужно сочетать с глобальным ограничением и кешированием. Точка зрения этого сайта, как и в статье Ограничение частоты запросов и защита от злоупотреблений, состоит в том, чтобы сделать злоупотребление невыгодным, а не сводить его успешность к нулю.
Источники (первичные)
- OWASP API Security Top 10 2023, «API4:2023 Unrestricted Resource Consumption» — api-security.owasp.org (ограничение частоты обращений клиента к API за период; настройка лимитов расходов на внешние сервисы)
- OWASP Automated Threats, «OAT-011 Scraping» — owasp.github.io (определяет скрейпинг как сбор контента или данных для использования в другом месте, например для ценового сговора; данные, не требующие аутентификации, могут быть целью)
Читайте далее
- Проектирование: Ограничение частоты запросов и защита от злоупотреблений: как проектировать лимиты, которые останавливают перебор и неконтролируемые расходы
- Предварительное чтение: Подмена X-Forwarded-For и доверенные прокси (основа подсчёта по IP)
- Эксплуатация: Базовый уровень организационной безопасности (как заметить, что что-то не так)