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 — держать соединение, отвечать медленно. Почему эффективнее блока:
- Бот не знает что обнаружен — получает 200, просто медленно. Нет сигнала менять IP.
- Занимает ресурсы бота — его потоки висят в ожидании. 10 параллельных запросов с задержкой 10 сек = 1 req/sec вместо 100.
- Автоматический бэкофф — многие библиотеки сами снижают rate при медленных ответах.
Tarpit как DoS
Tarpit держит Node.js-соединение. При массовой атаке — ограничить
MAX_TARPITS(например 100), при превышении — жёсткий 429.
Хранение состояния
| Решение | Когда |
|---|---|
| Map в памяти | 1 инстанс, < 100k IP. 50k записей ~ 20-50 MB |
| Redis | Несколько инстансов за балансером |
Защита от ложных срабатываний
- Авторизованные пользователи — score
*= 0.5, но не обнулять (см. авторизованные скрейперы) - Whitelist ботов — Googlebot, Yandexbot (проверять reverse DNS)
- Блок невозможен по одному сигналу — максимум одного = 30, порог блока = 80
- Первые 2 недели — режим “только логи”, не блокировать
- Decay критически важен — пользователь, быстро пролиставший 5 страниц, не блокируется навсегда
Кто похож на бота, но не бот
| Ситуация | Решение |
|---|---|
| RSS-ридеры | Whitelist известных UA |
| PWA / мобильные приложения | Cookie = легитимный, обнулить score |
| Корпоративный NAT (200 человек за 1 IP) | Не блокировать IP, только замедлять; учитывать cookie |
| Пользователь с adblocker | Нет статики ≠ бот, только +10-20 |
Связанные заметки
- Защита API от скрейпинга — общая стратегия
- HMAC-подпись API запросов — предыдущий уровень защиты
- CrowdSec — внешний инструмент для IP-репутации