Rate Limiting и DDoS-защита

Rate Limiting — ограничение числа запросов от одного источника за единицу времени; защита от DDoS (Distributed Denial of Service) включает более широкий набор мер — CDN, WAF, anycast routing — для поддержания доступности сервиса под атакой.

Зачем нужно

Без rate limiting API уязвимо к brute force паролей, перебору OTP, scrapin'у данных и исчерпанию ресурсов сервера. DDoS-атаки могут вывести сервис из строя за секунды без соответствующей защиты на уровне сети и приложения.

Где используется

  • Эндпоинты аутентификации (/login, /forgot-password, /verify-otp)
  • Публичные API с лимитами использования
  • Формы отправки писем и SMS-кодов
  • Любые resource-intensive операции (экспорт, парсинг, загрузка файлов)

Основной контент

express-rate-limit (базовый rate limiting)

const rateLimit = require('express-rate-limit');

// Общий лимит для всего приложения
const generalLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 минут
  max: 100,                  // 100 запросов с одного IP
  standardHeaders: true,     // Добавляет RateLimit-* заголовки
  legacyHeaders: false,
  message: { error: 'Too many requests, please try again later.' },
});

// Строгий лимит для логина
const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 5,
  skipSuccessfulRequests: true, // Не считать успешные запросы
});

app.use('/api/', generalLimiter);
app.post('/api/auth/login', loginLimiter, loginHandler);

Redis-backed rate limiting (распределённый)

const { RateLimiterRedis } = require('rate-limiter-flexible');
const redis = require('ioredis');

const limiter = new RateLimiterRedis({
  storeClient: redis,
  keyPrefix: 'login',
  points: 5,           // 5 попыток
  duration: 60 * 15,   // за 15 минут
  blockDuration: 60 * 60, // блокировать на 1 час
});

app.post('/login', async (req, res) => {
  try {
    await limiter.consume(req.ip);
    // продолжить авторизацию
  } catch (rejRes) {
    const secs = Math.round(rejRes.msBeforeNext / 1000) || 1;
    res.set('Retry-After', secs);
    res.status(429).json({ error: 'Too many login attempts' });
  }
});

Nginx rate limiting

# В http блоке
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;

# В server блоке
location /api/auth/login {
    limit_req zone=login burst=2 nodelay;
    limit_req_status 429;
    proxy_pass http://backend;
}

DDoS-защита: уровни

L3/L4 (сеть):  Cloudflare, AWS Shield — фильтрация по IP, протоколу
L7 (приложение): WAF — анализ HTTP, блокировка по паттернам
CDN:           Распределение трафика, поглощение объёмных атак
Anycast:       Распределение по PoP — атака "размывается"

Частые ошибки

  • Rate limiting только по IP — обходится через Tor/VPN/botnet
  • Отсутствие Retry-After заголовка — клиенты ретраят немедленно
  • In-memory rate limiter на несколько серверов — каждый считает независимо
  • Rate limiting на load balancer вместо application level — теряется контекст пользователя

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

Ресурсы