Тема
Режим
Язык
Тема
Режим
Язык
Регистрация
FREE Бесплатный аудит сайта за 15 мин Заказать →

API Rate Limiting: защита от abuse и DDoS

API Rate Limiting: защита от abuse и DDoS

Executive Summary для руководителя
💰

Финансовый риск

До 500 000 рублей в час ввиду парализации внутренних интеграций и сбоя в работе мобильных приложений. Риск каскадного отказа всех связанных сервисов предприятия.

📈

Влияние на KPI

Недоступность мобильного приложения для клиентов. Рост процента ошибок API (5xx). Снижение LTV и удовлетворенности пользователей из-за медленной работы интерфейсов.

⚠️

Уровень критичности

Высокий

👤

Кому поручить

Backend-разработчик / Архитектор API / DevOps-инженер

Slug: api-rate-limiting-guide
Кластер: Базовая защита
Время чтения: 12 мин

01

Meta

  • SEO Title: API Rate Limiting — полный гайд по защите API от DDoS и abuse
  • SEO Description: Как настроить rate limiting для REST API: примеры для nginx, Node.js, Python, Go. Защита от брутфорса, credential stuffing и L7-атак.
  • OG Title: API Rate Limiting: защита от abuse и DDoS
  • OG Description: Практический гайд по настройке rate limiting для API с примерами кода для nginx, Express, Flask и Go.

02

Введение

Rate Limiting — первая линия защиты API от:

  • L7-атак (HTTP flood)
  • Брутфорса и credential stuffing
  • Агрессивного парсинга
  • Случайного abuse (баги в клиентах)

Без rate limiting один клиент может положить весь сервис. С ним — получит 429 Too Many Requests и подождёт.


03

Стратегии Rate Limiting

1. Fixed Window

Самый простой: N запросов за период (минута/час).

┌─────────────────────────────────────┐
│ 12:00-12:01  │  100 запросов max    │
│ 12:01-12:02  │  100 запросов max    │
└─────────────────────────────────────┘

Проблема: burst на границе окон (200 запросов за 2 секунды).

2. Sliding Window

Скользящее окно — считает запросы за последние N секунд.

Сейчас: 12:00:30
Окно: 12:00:30 - 11:59:30 (последние 60 сек)

Лучше для защиты, но требует больше памяти.

3. Token Bucket

Токены накапливаются со временем, тратятся на запросы.

Bucket: 100 токенов max
Refill: 10 токенов/сек
Запрос: -1 токен

Идеально для API — позволяет burst, но ограничивает sustained load.

4. Leaky Bucket

Запросы в очередь, обрабатываются с фиксированной скоростью.

Хорош для выравнивания нагрузки, но добавляет latency.


04

Nginx: базовый Rate Limiting

Конфигурация

# /etc/nginx/nginx.conf

http {
    # Зона лимитов: 10MB памяти, ключ = IP
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
    
    # Для авторизации — строже
    limit_req_zone $binary_remote_addr zone=auth_limit:10m rate=1r/s;
    
    # По API-ключу (если есть)
    limit_req_zone $http_x_api_key zone=apikey_limit:10m rate=100r/s;
}

Применение к location

server {
    # Общий API
    location /api/ {
        limit_req zone=api_limit burst=20 nodelay;
        limit_req_status 429;
        
        proxy_pass http://backend;
    }
    
    # Авторизация — строго
    location /api/auth/ {
        limit_req zone=auth_limit burst=5;
        limit_req_status 429;
        
        # Добавляем задержку при превышении
        limit_req zone=auth_limit burst=5 delay=3;
        
        proxy_pass http://backend;
    }
    
    # Публичные данные — мягче
    location /api/public/ {
        limit_req zone=api_limit burst=50 nodelay;
        proxy_pass http://backend;
    }
}

Параметры

Сравнение решений

Параметр Популярный Cloudflare DDoS-Guard StormWall Qrator
rate=10r/s 10 запросов в секунду
burst=20 Очередь до 20 запросов
nodelay Не задерживать burst
delay=N Задержать после N запросов

* Бесплатный план — базовая поддержка. Полная 24/7 поддержка на Pro+ тарифах.


05

Node.js / Express

express-rate-limit

const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');
const Redis = require('ioredis');

const redis = new Redis(process.env.REDIS_URL);

// Общий лимит API
const apiLimiter = rateLimit({
  store: new RedisStore({ client: redis }),
  windowMs: 60 * 1000, // 1 минута
  max: 100, // 100 запросов
  standardHeaders: true, // RateLimit-* headers
  legacyHeaders: false,
  message: {
    error: 'Too many requests',
    retryAfter: 60
  },
  keyGenerator: (req) => {
    // По API-ключу или IP
    return req.headers['x-api-key'] || req.ip;
  }
});

// Строгий лимит для auth
const authLimiter = rateLimit({
  store: new RedisStore({ client: redis }),
  windowMs: 15 * 60 * 1000, // 15 минут
  max: 5, // 5 попыток
  skipSuccessfulRequests: true, // Не считать успешные
  message: {
    error: 'Too many login attempts',
    retryAfter: 900
  }
});

// Применение
app.use('/api/', apiLimiter);
app.use('/api/auth/login', authLimiter);

Динамические лимиты по плану

const getPlanLimits = (apiKey) => {
  const plans = {
    free: { rpm: 60, burst: 10 },
    pro: { rpm: 1000, burst: 100 },
    enterprise: { rpm: 10000, burst: 500 }
  };
  // Получить план из БД по apiKey
  return plans[getUserPlan(apiKey)] || plans.free;
};

const dynamicLimiter = rateLimit({
  store: new RedisStore({ client: redis }),
  windowMs: 60 * 1000,
  max: (req) => {
    const limits = getPlanLimits(req.headers['x-api-key']);
    return limits.rpm;
  },
  keyGenerator: (req) => req.headers['x-api-key'] || req.ip
});

06

Python / Flask

flask-limiter

from flask import Flask
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

app = Flask(__name__)

limiter = Limiter(
    app=app,
    key_func=get_remote_address,
    storage_uri="redis://localhost:6379",
    default_limits=["100 per minute"]
)

# Базовый endpoint
@app.route("/api/data")
@limiter.limit("100/minute")
def get_data():
    return {"data": "..."}

# Строгий лимит для auth
@app.route("/api/auth/login", methods=["POST"])
@limiter.limit("5/15 minutes")
def login():
    return {"token": "..."}

# Разные лимиты по роли
def get_limit_by_role():
    api_key = request.headers.get("X-API-Key")
    if is_premium(api_key):
        return "1000/minute"
    return "100/minute"

@app.route("/api/premium")
@limiter.limit(get_limit_by_role)
def premium_endpoint():
    return {"premium": True}

# Обработка 429
@app.errorhandler(429)
def ratelimit_error(e):
    return {
        "error": "Rate limit exceeded",
        "retry_after": e.description
    }, 429

07

Go

golang.org/x/time/rate

package main

import (
    "net/http"
    "sync"
    "time"
    
    "golang.org/x/time/rate"
)

type IPRateLimiter struct {
    ips map[string]*rate.Limiter
    mu  *sync.RWMutex
    r   rate.Limit
    b   int
}

func NewIPRateLimiter(r rate.Limit, b int) *IPRateLimiter {
    return &IPRateLimiter{
        ips: make(map[string]*rate.Limiter),
        mu:  &sync.RWMutex{},
        r:   r,
        b:   b,
    }
}

func (i *IPRateLimiter) GetLimiter(ip string) *rate.Limiter {
    i.mu.Lock()
    defer i.mu.Unlock()
    
    limiter, exists := i.ips[ip]
    if !exists {
        limiter = rate.NewLimiter(i.r, i.b)
        i.ips[ip] = limiter
    }
    return limiter
}

// Middleware
func RateLimitMiddleware(limiter *IPRateLimiter) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            ip := r.RemoteAddr
            if !limiter.GetLimiter(ip).Allow() {
                w.Header().Set("Retry-After", "60")
                http.Error(w, "Too Many Requests", http.StatusTooManyRequests)
                return
            }
            next.ServeHTTP(w, r)
        })
    }
}

func main() {
    // 10 запросов/сек, burst 20
    limiter := NewIPRateLimiter(rate.Limit(10), 20)
    
    mux := http.NewServeMux()
    mux.HandleFunc("/api/", handleAPI)
    
    http.ListenAndServe(":8080", RateLimitMiddleware(limiter)(mux))
}

08

Redis для распределённого Rate Limiting

При нескольких серверах нужен общий счётчик:

-- rate_limit.lua (Redis Lua script)
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])

local current = redis.call('INCR', key)

if current == 1 then
    redis.call('EXPIRE', key, window)
end

if current > limit then
    return 0  -- Rejected
end

return 1  -- Allowed
// Node.js
const checkRateLimit = async (key, limit, windowSec) => {
  const result = await redis.eval(
    rateLimitScript,
    1,
    `ratelimit:${key}`,
    limit,
    windowSec
  );
  return result === 1;
};

09

Headers для клиентов

Стандарт RFC 6585:

HTTP/1.1 429 Too Many Requests
Retry-After: 60
RateLimit-Limit: 100
RateLimit-Remaining: 0
RateLimit-Reset: 1640995200

{
  "error": "Rate limit exceeded",
  "retry_after": 60
}

Сравнение решений

Header Популярный Cloudflare DDoS-Guard StormWall Qrator
Retry-After Секунды до retry
RateLimit-Limit Максимум запросов
RateLimit-Remaining Осталось запросов
RateLimit-Reset Unix timestamp сброса

* Бесплатный план — базовая поддержка. Полная 24/7 поддержка на Pro+ тарифах.


10

Защита от обхода

1. Не только по IP

const keyGenerator = (req) => {
  const factors = [
    req.ip,
    req.headers['x-api-key'],
    req.headers['user-agent'],
    req.user?.id
  ].filter(Boolean);
  
  return crypto.createHash('sha256')
    .update(factors.join(':'))
    .digest('hex');
};

2. Прогрессивные задержки

const getDelay = (attempts) => {
  // 0, 1, 2, 4, 8, 16... секунд
  return Math.min(Math.pow(2, attempts - 1), 60) * 1000;
};

3. CAPTCHA при подозрении

if (attempts > 3 && !req.captchaVerified) {
  return res.status(429).json({
    error: 'CAPTCHA required',
    captcha_url: '/api/captcha'
  });
}

11

Мониторинг

Prometheus метрики

const rateLimitCounter = new prometheus.Counter({
  name: 'api_rate_limit_hits_total',
  help: 'Rate limit hits',
  labelNames: ['endpoint', 'status']
});

// В middleware
if (rateLimitExceeded) {
  rateLimitCounter.inc({ endpoint: req.path, status: 'rejected' });
} else {
  rateLimitCounter.inc({ endpoint: req.path, status: 'allowed' });
}

Grafana алерты

# Alert при >5% rejected запросов
- alert: HighRateLimitRejection
  expr: |
    sum(rate(api_rate_limit_hits_total{status="rejected"}[5m]))
    /
    sum(rate(api_rate_limit_hits_total[5m]))
    > 0.05
  for: 5m
  annotations:
    summary: "High rate limit rejection rate"

12

Чек-лист внедрения

Чек-лист готовности

0 / 8

13

Связанные материалы

14

Практическая экспертиза AntiDDoS.su

📝
Заметка

Практическая экспертиза AntiDDoS.su Этот материал подготовлен инженерами безопасности AntiDDoS.su. Мы специализируемся на отражении распределенных атак любой сложности, аудите безопасности инфраструктуры и настройке отказоустойчивых систем. 🛡️ Важная информация: Если ваш ресурс находится под атакой или нуждается в профессиональном аудите защиты — обратитесь к экспертам AntiDDoS.su для оперативной помощи.


Чек-лист проверки для владельца бизнеса

Скопируйте эти вопросы и отправьте вашему техническому директору (CTO) или руководителю разработки:

  • Внедрен ли паттерн Circuit Breaker для изоляции упавших сервисов и сохранения стабильности остальной системы?
  • Лимитируются ли запросы к API по токенам авторизации (JWT) на уровне Ingress/API Gateway?
  • Ограничена ли глубина и сложность запросов в GraphQL для защиты от атак на исчерпание памяти?

Словарь по теме

Rate Limiting

Ограничение количества запросов с одного IP-адреса за определённый период времени. Первая линия защиты от ботов, брутфорса и простых DDoS-атак.

User-Agent

HTTP-заголовок, идентифицирующий браузер или программу. Боты часто имеют пустой или подозрительный User-Agent.

Prometheus

Система мониторинга и сбора метрик. Собирает данные с серверов, хранит временные ряды, поддерживает алерты.

L7 атака

Атака на прикладном уровне (HTTP). Атакующий отправляет валидные HTTP-запросы, которые выглядят как обычные пользователи, но нагружают тяжёлые endpoint'ы.

Endpoint

URL-адрес, по которому доступен определённый ресурс или функция API. Например: /api/users, /login.

Latency

Время отклика — задержка между отправкой запроса и получением ответа. Измеряется в миллисекундах. Чем меньше — тем лучше.

CAPTCHA

Тест для отличия человека от бота. Просит распознать изображения, решить задачу или просто кликнуть галочку. Используется для защиты форм.

Grafana

Платформа визуализации метрик. Строит дашборды с графиками из Prometheus, InfluxDB и других источников.

Получите план защиты под ваш сайт

Оставьте контакт и адрес сайта — пришлём план защиты и список приоритетных шагов.

  • Приоритетные шаги на 7 дней
  • Быстрая обратная связь
  • План в удобном формате
Без спама. Можно указать Telegram (@username) или email.
Написать в Telegram