Self-built middleware, которое считает “подозрительность” каждого IP по набору сигналов и принимает решение: пропустить, замедлить (tarpit) или заблокировать.

Сигналы

Tier 1 — проверяются на каждый запрос (заголовки)

СигналЧто значитВес
Нет User-AgentБраузер всегда шлёт+30
Нет Accept-LanguageБраузер всегда шлёт+15
Accept: */*Браузер шлёт конкретные MIME+10
API-запрос без RefererНе из браузера+10
Нет загрузки статики (CSS/JS)Скрипт, а не браузер+20

Tier 2 — требуют хранения состояния per-IP

СигналЧто значитВес
> 30 req/minСлишком быстро для человека+25
Последовательная пагинация (page=1,2,3…)Типичный скрейпер+25
Регулярные интервалы (stddev < 100ms)Робот+20
Обход всех ID подряд (/route/1, /route/2…)Полный дамп+30
Много запросов без cookie/sessionНе пользователь+15

Система скоринга

Накопительный score с decay:

Каждый запрос:
  1. Вычислить мгновенный score по сигналам
  2. Прибавить к accumulated score для IP
  3. Decay: каждую минуту score *= 0.95 (за час обнуляется)

Пороги:
  < 30   → нормальный ответ
  30-60  → мягкий tarpit (задержка 1-3 сек)
  60-80  → агрессивный tarpit (5-15 сек)
  > 80   → блок 429 на 10 минут

Tarpit

Вместо 403 — держать соединение, отвечать медленно. Почему эффективнее блока:

  1. Бот не знает что обнаружен — получает 200, просто медленно. Нет сигнала менять IP.
  2. Занимает ресурсы бота — его потоки висят в ожидании. 10 параллельных запросов с задержкой 10 сек = 1 req/sec вместо 100.
  3. Автоматический бэкофф — многие библиотеки сами снижают rate при медленных ответах.

Tarpit как DoS

Tarpit держит Node.js-соединение. При массовой атаке — ограничить MAX_TARPITS (например 100), при превышении — жёсткий 429.

Хранение состояния

РешениеКогда
Map в памяти1 инстанс, < 100k IP. 50k записей ~ 20-50 MB
RedisНесколько инстансов за балансером

Защита от ложных срабатываний

  1. Авторизованные пользователи — score *= 0.5, но не обнулять (см. авторизованные скрейперы)
  2. Whitelist ботов — Googlebot, Yandexbot (проверять reverse DNS)
  3. Блок невозможен по одному сигналу — максимум одного = 30, порог блока = 80
  4. Первые 2 недели — режим “только логи”, не блокировать
  5. Decay критически важен — пользователь, быстро пролиставший 5 страниц, не блокируется навсегда

Кто похож на бота, но не бот

СитуацияРешение
RSS-ридерыWhitelist известных UA
PWA / мобильные приложенияCookie = легитимный, обнулить score
Корпоративный NAT (200 человек за 1 IP)Не блокировать IP, только замедлять; учитывать cookie
Пользователь с adblockerНет статики ≠ бот, только +10-20

Связанные заметки