Webhooks expliqués : charges utiles signées et stratégies de réessai
Vous venez d'intégrer un service tiers qui envoie des webhooks, et tout fonctionne bien en test. Mais en production, vous remarquez des événements manqués occasionnels, des livraisons en double, ou pire, un acteur malveillant envoyant de fausses charges utiles à votre point de terminaison. Les webhooks sont simples en concept mais délicats en pratique. Cet article explique comment sécuriser les webhooks avec des charges utiles signées et mettre en œuvre des stratégies de réessai robustes afin que vous puissiez faire confiance à vos intégrations pilotées par les événements.
Que sont les webhooks et pourquoi ils importent
Les webhooks sont des callbacks HTTP définis par l'utilisateur, déclenchés par des événements spécifiques. Lorsqu'un événement se produit—comme un paiement réussi ou une nouvelle inscription d'utilisateur—le service source envoie une requête HTTP POST à une URL que vous fournissez. Ce modèle basé sur le push est plus efficace que le polling, réduisant la latence et la charge serveur. Cependant, il introduit des défis : vérifier l'authenticité et gérer les échecs de livraison.
Sécuriser les webhooks avec des charges utiles signées
Sans vérification, n'importe qui peut envoyer des requêtes à votre point de terminaison de webhook. Les charges utiles signées résolvent ce problème en incluant une signature cryptographique qui prouve que la requête provient de l'expéditeur de confiance.
Comment fonctionnent les signatures HMAC
La plupart des fournisseurs utilisent HMAC (Hash-based Message Authentication Code) avec un secret partagé. L'expéditeur calcule un hachage de la charge utile en utilisant le secret et l'inclut dans un en-tête (par exemple, X-Signature). Votre serveur recalcule le hachage et le compare. S'ils correspondent, la requête est authentique.
// Exemple : Vérification d'une signature de webhook en Node.js
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
hmac.update(payload);
const digest = hmac.digest('hex');
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
}
Important : Utilisez toujours une comparaison à temps constant pour éviter les attaques temporelles. Vérifiez également la signature par rapport au corps brut de la requête, et non à un objet JSON analysé, car l'analyse peut altérer la charge utile.
Meilleures pratiques pour la vérification de signature
- Utilisez HTTPS : Servez toujours les webhooks via TLS pour éviter l'écoute clandestine.
- Faites tourner les secrets : Faites tourner périodiquement votre secret de signature et prenez en charge plusieurs secrets actifs pendant la rotation.
- Validez l'horodatage : Incluez un horodatage dans la signature pour éviter les attaques par rejeu. Rejetez les requêtes plus anciennes que quelques minutes.
- Journalisez les échecs : Surveillez les échecs de vérification de signature pour détecter les attaques ou les erreurs de configuration.
Mettre en œuvre des réessais fiables
Même avec des charges utiles signées, des problèmes réseau ou des pannes temporaires peuvent entraîner l'échec de la livraison des webhooks. Une stratégie de réessai robuste garantit que les événements sont finalement livrés sans surcharger votre système.
Backoff exponentiel avec jitter
Réessayer immédiatement peut aggraver la congestion. Au lieu de cela, utilisez un backoff exponentiel : attendez plus longtemps entre chaque réessai. Ajoutez du jitter (variation aléatoire) pour éviter les problèmes de troupeau tonnant lorsque de nombreux webhooks échouent simultanément.
// Exemple : Calendrier de réessai avec backoff exponentiel et jitter
function getRetryDelay(attempt) {
const base = 1000; // 1 seconde
const max = 30000; // 30 secondes
const delay = Math.min(base * Math.pow(2, attempt), max);
return delay + Math.random() * 1000; // ajoute jusqu'à 1s de jitter
}
Idempotence : gérer les doublons
Les réessais peuvent entraîner des livraisons en double. Votre gestionnaire de webhook doit être idempotent—traiter le même événement plusieurs fois doit avoir le même effet que le traiter une fois. Utilisez l'ID d'événement de la charge utile pour suivre les événements traités.
- Stockez les ID d'événements traités dans une base de données avec une TTL.
- Avant le traitement, vérifiez si l'ID d'événement existe déjà ; si c'est le cas, renvoyez immédiatement une réponse de succès.
- Concevez les opérations pour qu'elles soient idempotentes (par exemple, utilisez des upserts au lieu d'inserts).
Répondez rapidement et accusez réception
Votre point de terminaison de webhook doit répondre avec un code de statut 2xx aussi rapidement que possible—idéalement en quelques secondes. Si le traitement prend plus de temps, mettez l'événement en file d'attente pour un traitement asynchrone et renvoyez 202 Accepted. Cela évite les délais d'attente et les réessais inutiles.
Comparaison : Webhook vs. Polling
| Aspect | Webhooks | Polling |
|---|---|---|
| Latence | Quasi temps réel | Dépend de l'intervalle |
| Charge serveur | Faible (push uniquement) | Élevée (requêtes constantes) |
| Complexité | Plus élevée (nécessite des réessais, sécurité) | Plus faible |
| Fiabilité | Nécessite une logique de réessai | Intrinsèquement fiable |
Étape par étape : construire un récepteur de webhook sécurisé
- Configurez un point de terminaison HTTPS avec un certificat valide.
- Extrayez l'en-tête de signature (par exemple,
X-Signature) et l'horodatage. - Vérifiez la signature en utilisant le secret partagé et le corps brut.
- Vérifiez l'horodatage pour rejeter les requêtes anciennes (par exemple, plus de 5 minutes).
- Analysez la charge utile et vérifiez les ID d'événements en double.
- Traitez l'événement de manière asynchrone si nécessaire, et répondez avec 200 OK.
- Journalisez l'événement pour l'audit et le débogage.
FAQ
Qu'est-ce qu'une charge utile de webhook signée ?
Une charge utile signée inclut une signature cryptographique (généralement HMAC) qui permet au récepteur de vérifier que la requête provient d'une source de confiance et n'a pas été altérée.
Combien de fois dois-je réessayer un webhook échoué ?
La plupart des systèmes réessaient 3 à 5 fois avec un backoff exponentiel. Ensuite, l'événement est généralement marqué comme échoué et peut nécessiter une intervention manuelle ou une gestion par file d'attente de lettres mortes.
Puis-je utiliser des webhooks sans HTTPS ?
C'est fortement déconseillé. Sans HTTPS, les charges utiles et les signatures peuvent être interceptées ou modifiées. Utilisez toujours TLS pour les points de terminaison de webhook.
Lors du débogage de charges utiles ou de journaux de webhooks, un formatage JSON correct est essentiel. Utilisez notre JSON Formatter pour imprimer joliment et valider rapidement les charges utiles JSON.