Limitation de débit des API : algorithmes et implémentations
Votre API est attaquée. Pas par des pirates sophistiqués, mais par un simple script qui martèle vos points de terminaison des milliers de fois par seconde, consommant des ressources et dégradant le service pour les utilisateurs légitimes. Sans limitation de débit, un seul client malveillant peut faire tomber toute votre application. Ce guide explique les algorithmes de base et montre des implémentations pratiques en Node.js et Nginx pour protéger votre API.
Pourquoi la limitation de débit est importante
La limitation de débit contrôle le nombre de requêtes qu'un client peut effectuer dans une fenêtre de temps donnée. C'est votre première ligne de défense contre :
- Les attaques par force brute sur les points de terminaison de connexion ou de clé API
- Les attaques par déni de service (DoS) dues à des requêtes excessives
- L'épuisement des ressources dû à des opérations coûteuses comme les requêtes de base de données ou le traitement de fichiers
- L'abus des offres gratuites par des scrapers ou des bots
Au-delà de la sécurité, la limitation de débit garantit une utilisation équitable et vous aide à appliquer des règles métier, telles que les plans tarifaires à plusieurs niveaux.
Algorithmes de limitation de débit
Quatre algorithmes dominent la limitation de débit des API. Chacun présente des compromis en termes de précision, d'utilisation de la mémoire et de gestion des rafales.
1. Fenêtre fixe
Comptez les requêtes dans des intervalles de temps fixes (par exemple, 100 requêtes par minute). Lorsque la fenêtre se réinitialise, le compteur se réinitialise.
- Avantages : Simple, faible consommation de mémoire (un compteur par client).
- Inconvénients : Rafale aux limites de la fenêtre. Un client peut envoyer 100 requêtes à 12:00:59 et 100 autres à 12:01:00, soit 200 requêtes en deux secondes.
2. Fenêtre glissante
Suivez les horodatages de chaque requête et comptez combien tombent dans les N dernières secondes. Cela lisse les rafales.
- Avantages : Précis, pas de pics aux limites.
- Inconvénients : Utilisation mémoire plus élevée (stockage des horodatages) ou utilisez un compteur de fenêtre glissante avec moyenne pondérée.
3. Token Bucket
Un seau contient des jetons. Les jetons sont ajoutés à un rythme fixe. Chaque requête consomme un jeton. Si le seau est vide, la requête est refusée.
- Avantages : Permet des rafales jusqu'à la taille du seau, puis applique le taux moyen.
- Inconvénients : Nécessite de stocker le nombre de jetons et l'heure du dernier remplissage par client.
4. Leaky Bucket
Les requêtes entrent dans une file d'attente (seau) et sont traitées à un rythme constant. Si la file d'attente est pleine, les requêtes sont abandonnées.
- Avantages : Lisse le trafic à un rythme régulier, idéal pour protéger les services en aval.
- Inconvénients : Ajoute de la latence ; ne convient pas aux API en temps réel.
| Algorithme | Gestion des rafales | Mémoire | Cas d'utilisation |
|---|---|---|---|
| Fenêtre fixe | Faible | Basse | API simples |
| Fenêtre glissante | Bonne | Moyenne | Usage général |
| Token Bucket | Excellente | Moyenne | API avec tolérance aux rafales |
| Leaky Bucket | Aucune | Moyenne | Lissage du trafic |
Implémentation de la limitation de débit en Node.js
Nous allons implémenter un limiteur de débit token bucket en utilisant Express et Redis pour un état distribué. Redis est essentiel lorsque vous avez plusieurs instances de serveur.
Étape 1 : Installer les dépendances
npm install express redis
Étape 2 : Créer le middleware de limitation de débit
const redis = require('redis');
const client = redis.createClient();
async function tokenBucketLimiter(req, res, next) {
const key = `rate_limit:${req.ip}`;
const capacity = 10; // max tokens
const refillRate = 1; // tokens per second
const now = Date.now();
const data = await client.hGetAll(key);
let tokens = data.tokens ? parseFloat(data.tokens) : capacity;
let lastRefill = data.lastRefill ? parseInt(data.lastRefill) : now;
// Refill tokens based on elapsed time
const elapsed = (now - lastRefill) / 1000;
tokens = Math.min(capacity, tokens + elapsed * refillRate);
if (tokens < 1) {
return res.status(429).json({ error: 'Too many requests' });
}
tokens -= 1;
await client.hSet(key, {
tokens: tokens.toString(),
lastRefill: now.toString()
});
await client.expire(key, 60); // auto-cleanup
next();
}
app.use(tokenBucketLimiter);
Ce middleware vérifie et met à jour le nombre de jetons de manière atomique. Pour la production, utilisez des transactions Redis ou des scripts Lua pour éviter les conditions de course.
Implémentation de la limitation de débit dans Nginx
Nginx offre une limitation de débit intégrée avec le module limit_req. Il utilise un algorithme de leaky bucket.
Étape 1 : Définir une zone de limitation de débit
Dans le bloc http de nginx.conf :
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
Cela crée une zone de 10 Mo nommée api qui autorise 10 requêtes par seconde par IP.
Étape 2 : Appliquer la limite
Dans votre bloc location :
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://backend;
}
burst=20 autorise des rafales courtes jusqu'à 20 requêtes. nodelay traite les requêtes en rafale immédiatement au lieu de les mettre en file d'attente.
Bonnes pratiques pour la limitation de débit des API
- Retournez les codes de statut appropriés : Utilisez
429 Too Many Requestset incluez l'en-têteRetry-After. - Identifiez correctement les clients : Utilisez des clés API ou des ID utilisateur au lieu des adresses IP lorsque c'est possible, car les IP peuvent être partagées (NAT) ou usurpées.
- Distribuez l'état : Utilisez Redis ou un stockage similaire pour les déploiements multi-instances.
- Journalisez et surveillez : Suivez les atteintes à la limitation de débit pour détecter les attaques et ajuster les seuils. Des outils comme le Nginx Log Analyzer peuvent vous aider à analyser les réponses 429 et à identifier les IP abusives.
- Communiquez les limites : Documentez les limites de débit dans votre documentation API et incluez des en-têtes comme
X-RateLimit-Limit,X-RateLimit-Remaining.
FAQ
Quelle est la différence entre limitation de débit et throttling ?
La limitation de débit bloque les requêtes au-delà d'un seuil, tandis que le throttling les ralentit (par exemple, en les mettant en file d'attente ou en les retardant). La limitation de débit est binaire ; le throttling est progressif.
Quel algorithme de limitation de débit dois-je utiliser ?
Pour la plupart des API, le token bucket offre un bon équilibre : il permet des rafales mais applique un taux moyen. Si vous avez besoin d'un lissage strict, utilisez le leaky bucket. Pour plus de simplicité, la fenêtre fixe fonctionne pour les API à faible trafic.
Comment gérer la limitation de débit pour les utilisateurs authentifiés vs anonymes ?
Appliquez des limites plus strictes aux utilisateurs anonymes (par exemple, par IP) et des limites plus généreuses aux utilisateurs authentifiés (par exemple, par clé API). Vous pouvez également mettre en place des limites par paliers en fonction des plans d'abonnement.
Prêt à analyser votre trafic API ? Utilisez notre Nginx Log Analyzer pour analyser les journaux, repérer les violations de limitation de débit et optimiser vos seuils.