Webhooks expliqués : charges utiles signées et nouvelles tentatives
Vous venez d'intégrer le webhook d'un fournisseur de paiement pour recevoir les mises à jour de transactions. Tout fonctionne en test, mais en production vous remarquez des événements manquants lors des pics de trafic ou des doublons de débit lorsque le fournisseur réessaie. Les webhooks sont simples en concept mais délicats en pratique : ils traversent les frontières du réseau, peuvent être retardés, dupliqués ou usurpés. Cet article explique comment construire des consommateurs et des producteurs de webhooks robustes en utilisant des charges utiles signées et des stratégies de nouvelle tentative intelligentes.
Que sont les webhooks ?
Les webhooks sont des callbacks HTTP définis par l'utilisateur, déclenchés par des événements. Lorsqu'un événement se produit dans un système source (par exemple, une nouvelle commande), il envoie une requête HTTP POST à une URL que vous fournissez. Contrairement au polling, les webhooks poussent les données en quasi temps réel, réduisant la latence et la charge serveur.
Cependant, les webhooks introduisent des défis : s'assurer que la requête provient bien de l'expéditeur attendu, gérer les échecs avec élégance et éviter le traitement en double.
Sécuriser les webhooks avec des charges utiles signées
Sans vérification, n'importe qui peut envoyer de fausses requêtes à votre point de terminaison de webhook. Les charges utiles signées résolvent ce problème en incluant une signature cryptographique que vous pouvez valider.
Comment fonctionnent les signatures HMAC
L'expéditeur calcule un code d'authentification de message basé sur un hachage (HMAC) en utilisant un secret partagé et le corps de la requête. La signature est envoyée dans un en-tête (par exemple, X-Signature). Vous recalculez le HMAC de votre côté et comparez.
// Exemple Node.js : vérification d'une signature de webhook
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
Utilisez toujours une fonction de comparaison à temps constant comme timingSafeEqual pour éviter les attaques temporelles.
Inclure un horodatage
Pour éviter les attaques par rejeu, incluez un horodatage dans la charge utile signée et rejetez les requêtes plus anciennes que quelques minutes (par exemple, 5 minutes). L'horodatage doit faire partie des données signées.
Implémenter des nouvelles tentatives fiables
Des problèmes réseau ou des erreurs serveur temporaires peuvent entraîner l'échec de la livraison du webhook. Un expéditeur robuste réessaie les livraisons échouées avec un backoff exponentiel.
Stratégie de nouvelle tentative
- Backoff exponentiel : Attendez plus longtemps entre chaque tentative (par exemple, 1s, 2s, 4s, 8s...).
- Nombre maximal de tentatives : Fixez une limite (par exemple, 5 tentatives) pour éviter les boucles infinies.
- File d'attente de messages morts : Après le nombre maximal de tentatives, stockez l'événement pour inspection manuelle.
- Idempotence : Incluez un ID d'événement unique pour que le récepteur puisse dédupliquer.
Exemple : Nouvelle tentative avec backoff en Python
import time
import requests
from requests.exceptions import RequestException
def send_webhook(url, payload, max_retries=5):
for attempt in range(max_retries):
try:
response = requests.post(url, json=payload, timeout=5)
if response.status_code < 500:
return True # succès ou erreur client (pas de nouvelle tentative)
except RequestException:
pass
time.sleep(2 ** attempt) # backoff exponentiel
return False
Bonnes pratiques pour les consommateurs de webhooks
- Répondez rapidement : Accusez réception avec un statut 2xx avant de traiter. Mettez le travail en file d'attente de manière asynchrone.
- Validez les signatures : Vérifiez toujours la signature avant de faire confiance à la charge utile.
- Gérez les doublons : Utilisez l'ID d'événement pour garantir un traitement idempotent.
- Journalisez tout : Conservez des journaux des webhooks reçus pour le débogage.
Comparaison : Webhooks vs Polling
| Aspect | Webhooks | Polling |
|---|---|---|
| Latence | Quasi temps réel | Dépend de l'intervalle |
| Charge serveur | Faible (push uniquement) | Élevée (requêtes fréquentes) |
| Complexité | Plus élevée (nécessite un point de terminaison, sécurité) | Plus faible |
| Fiabilité | Nécessite des nouvelles tentatives | Nouvelles tentatives inhérentes |
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 l'authenticité et l'intégrité de la requête.
Combien de fois dois-je réessayer un webhook échoué ?
Une pratique courante est de 3 à 5 tentatives avec backoff exponentiel, mais cela dépend de votre SLA et de la criticité de l'événement.
Puis-je utiliser des webhooks sans HTTPS ?
Non, utilisez toujours HTTPS pour protéger la charge utile et la signature contre l'interception. HTTP en clair expose des données sensibles.
Lors du débogage des charges utiles de webhooks, vous pourriez avoir besoin de formater rapidement du JSON. Essayez notre JSON Formatter pour embellir et valider les réponses JSON.