Rate Limiting in APIs: Schutz vor Missbrauch

Rate Limiting ist eine grundlegende Technik, um APIs vor Missbrauch, übermäßigem Ressourcenverbrauch und Denial-of-Service-Angriffen zu schützen. Eine angemessene Implementierung gewährleistet die Verfügbarkeit für legitime Benutzer und blockiert gleichzeitig bösartiges oder übermäßiges Verhalten.

Warum Rate Limiting Implementieren?

  • Schutz vor DDoS: Denial-of-Service-Angriffe abwehren
  • Scraping Verhindern: Automatisierte Datenextraktion erschweren
  • Kosten Kontrollieren: Übermäßigen Verbrauch von Rechenressourcen vermeiden
  • Dienstqualität Sicherstellen: Ressourcen gerecht verteilen
  • Brute Force Verhindern: Authentifizierungsversuche begrenzen

Rate-Limiting-Algorithmen

1. Token Bucket

Ein Algorithmus, der einen „Eimer" mit Tokens unterhält, die sich im Laufe der Zeit wieder auffüllen:

      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);
      

Vorteile: Ermöglicht kontrollierte Bursts, glättet den Datenverkehr

Nachteile: Komplexer zu implementieren

2. Leaky Bucket

Verarbeitet Anfragen mit konstanter Rate, wie Wasser, das aus einem Eimer ausläuft:

  • Anfragen gelangen in den Eimer
  • Mit fester Rate verarbeitet
  • Überschuss läuft über (abgelehnt)
  • Gewährleistet eine gleichmäßige Ausgabe

3. Fixed Window

Zählt Anfragen innerhalb fester Zeitfenster:

      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);
      

Problem: Erlaubt bis zum 2-fachen des Limits an den Fenstergrenzen

4. Sliding Window Log

Unterhält ein Protokoll der Anfrage-Zeitstempel:

  • Speichert einen Zeitstempel für jede Anfrage
  • Entfernt Anfragen außerhalb des Fensters
  • Genauer als Fixed Window
  • Höherer Speicherverbrauch

5. Sliding Window Counter

Kombiniert Fixed Window mit Glättung:

      // 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;
      

Praktische Implementierung

Mit Redis (Empfohlen für Produktion)

      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();
      });
      

Beliebte Bibliotheken

  • express-rate-limit: Middleware für Express.js
  • rate-limiter-flexible: Unterstützt mehrere Backends (Redis, Memcached, MySQL)
  • Kong Rate Limiting: Plugin für API Gateway
  • AWS API Gateway: Natives Rate Limiting

Erweiterte Strategien

Hierarchisches Rate Limiting

  • Global: Gesamtlimit der API (z. B.: 1M req/min)
  • Pro Benutzer: Individuelles Limit (z. B.: 1000 req/min)
  • Pro Endpoint: Spezifische Limits (login: 5 req/min)
  • Pro IP: Zusätzlicher Schutz vor Missbrauch

Dynamisches Rate Limiting

  • Limits basierend auf der Systemlast anpassen
  • Limits für Premium-Benutzer erhöhen
  • Limits während Vorfällen reduzieren

Whitelisting und Blacklisting

  • Vertrauenswürdige IPs/Benutzer ausnehmen
  • Bekannte Angreifer dauerhaft blockieren
  • Ein Reputationssystem implementieren

Bewährte Verfahren

  • Informative Header zurückgeben (X-RateLimit-*)
  • HTTP-Status 429 (Too Many Requests) verwenden
  • Einen Retry-After-Header einschließen
  • Limits in der API klar dokumentieren
  • Exponentielles Backoff auf dem Client implementieren
  • Rate-Limiting-Metriken überwachen
  • Bei abnormalen Mustern alarmieren
  • Limits vor der Produktion testen

Überwachungstools

  • Grafana + Prometheus: Rate-Limiting-Metriken visualisieren
  • Datadog: Überwachung und Alarme
  • CloudWatch: Für APIs auf AWS
  • New Relic: APM mit Rate-Limiting-Unterstützung