Rate Limiting dans les API : Protection Contre les Abus

Le rate limiting est une technique essentielle pour protéger les API contre les abus, la consommation excessive de ressources et les attaques par déni de service. Une implémentation adéquate garantit la disponibilité pour les utilisateurs légitimes tout en bloquant les comportements malveillants ou excessifs.

Pourquoi Implémenter le Rate Limiting ?

  • Protection Contre le DDoS : Atténuer les attaques par déni de service
  • Empêcher le Scraping : Compliquer l'extraction automatisée de données
  • Maîtriser les Coûts : Éviter la consommation excessive de ressources de calcul
  • Garantir la Qualité de Service : Répartir les ressources de manière équitable
  • Empêcher le Brute Force : Limiter les tentatives d'authentification

Algorithmes de Rate Limiting

1. Token Bucket

Algorithme qui maintient un « seau » de tokens qui se remplissent au fil du temps :

      class TokenBucket {
      constructor(capacity, refillRate) {
      this.capacity = capacity;        // Capacidade máxima do balde
      this.tokens = capacity;          // Tokens disponíveis
      this.refillRate = refillRate;    // Tokens por segundo
      this.lastRefill = Date.now();
      }
      tryConsume(tokens = 1) {
      this.refill();
      if (this.tokens >= tokens) {
      this.tokens -= tokens;
      return true;  // Requisição permitida
      }
      return false;   // Rate limit excedido
      }
      refill() {
      const now = Date.now();
      const timePassed = (now - this.lastRefill) / 1000;
      const tokensToAdd = timePassed * this.refillRate;
      this.tokens = Math.min(this.capacity, this.tokens + tokensToAdd);
      this.lastRefill = now;
      }
      }
      // Uso: 100 requisições máximo, recarrega 10/segundo
      const bucket = new TokenBucket(100, 10);
      

Avantages : Permet des bursts contrôlés, lisse le trafic

Inconvénients : Plus complexe à implémenter

2. Leaky Bucket

Traite les requêtes à un débit constant, comme de l'eau qui s'écoule d'un seau :

  • Les requêtes entrent dans le seau
  • Traitées à un débit fixe
  • L'excédent déborde (rejeté)
  • Garantit une sortie uniforme

3. Fixed Window

Compte les requêtes dans des fenêtres de temps fixes :

      class FixedWindowRateLimiter {
      constructor(maxRequests, windowMs) {
      this.maxRequests = maxRequests;
      this.windowMs = windowMs;
      this.requests = new Map();
      }
      isAllowed(userId) {
      const now = Date.now();
      const windowStart = Math.floor(now / this.windowMs) * this.windowMs;
      const key = \`\$:\$\`;
      const count = this.requests.get(key) || 0;
      if (count < this.maxRequests) {
      this.requests.set(key, count + 1);
      return true;
      }
      return false;
      }
      }
      // 100 requisições por hora
      const limiter = new FixedWindowRateLimiter(100, 60 * 60 * 1000);
      

Problème : Permet jusqu'à 2x la limite aux bords des fenêtres

4. Sliding Window Log

Maintient un journal des horodatages des requêtes :

  • Stocke un horodatage pour chaque requête
  • Supprime les requêtes hors de la fenêtre
  • Plus précis que Fixed Window
  • Consommation de mémoire plus élevée

5. Sliding Window Counter

Combine Fixed Window avec un lissage :

      // Calcula uma média ponderada entre janelas atual e anterior
      const currentWindowCount = getCurrentWindowCount(userId);
      const previousWindowCount = getPreviousWindowCount(userId);
      const percentageInCurrentWindow = (now - currentWindowStart) / windowSize;
      const estimatedCount =
      previousWindowCount * (1 - percentageInCurrentWindow) +
      currentWindowCount;
      return estimatedCount < maxRequests;
      

Implémentation Pratique

Avec Redis (Recommandé en Production)

      import Redis from 'ioredis';
      const redis = new Redis();
      async function checkRateLimit(userId, maxRequests = 100, windowSeconds = 60) {
      const key = \`rate_limit:\$\`;
      const now = Date.now();
      const windowStart = now - (windowSeconds * 1000);
      // Remover requisições antigas
      await redis.zremrangebyscore(key, 0, windowStart);
      // Contar requisições na janela
      const requestCount = await redis.zcard(key);
      if (requestCount < maxRequests) {
      // Adicionar nova requisição
      await redis.zadd(key, now, \`\$-\${Math.random()}\`);
      await redis.expire(key, windowSeconds);
      return { allowed: true, remaining: maxRequests - requestCount - 1 };
      }
      return { allowed: false, remaining: 0 };
      }
      // Middleware Express
      app.use(async (req, res, next) => {
      const userId = req.user?.id || req.ip;
      const result = await checkRateLimit(userId);
      res.set({
      'X-RateLimit-Limit': 100,
      'X-RateLimit-Remaining': result.remaining,
      'X-RateLimit-Reset': new Date(Date.now() + 60000).toISOString()
      });
      if (!result.allowed) {
      return res.status(429).json({
      error: 'Too Many Requests',
      retryAfter: 60
      });
      }
      next();
      });
      

Bibliothèques Populaires

  • express-rate-limit : Middleware pour Express.js
  • rate-limiter-flexible : Prend en charge plusieurs backends (Redis, Memcached, MySQL)
  • Kong Rate Limiting : Plugin pour API Gateway
  • AWS API Gateway : Rate limiting natif

Stratégies Avancées

Rate Limiting Hiérarchique

  • Global : Limite totale de l'API (ex. : 1M req/min)
  • Par Utilisateur : Limite individuelle (ex. : 1000 req/min)
  • Par Endpoint : Limites spécifiques (login : 5 req/min)
  • Par IP : Protection supplémentaire contre les abus

Rate Limiting Dynamique

  • Ajuster les limites en fonction de la charge du système
  • Augmenter les limites pour les utilisateurs premium
  • Réduire les limites pendant les incidents

Whitelisting et Blacklisting

  • Exempter les IP/utilisateurs de confiance
  • Bloquer définitivement les attaquants connus
  • Implémenter un système de réputation

Meilleures Pratiques

  • Retourner des en-têtes informatifs (X-RateLimit-*)
  • Utiliser le statut HTTP 429 (Too Many Requests)
  • Inclure un en-tête Retry-After
  • Documenter clairement les limites dans l'API
  • Implémenter un backoff exponentiel côté client
  • Surveiller les métriques de rate limiting
  • Alerter sur les schémas anormaux
  • Tester les limites avant la production

Outils de Surveillance

  • Grafana + Prometheus : Visualiser les métriques de rate limiting
  • Datadog : Surveillance et alertes
  • CloudWatch : Pour les API sur AWS
  • New Relic : APM avec prise en charge du rate limiting