Webhooks expliqués : signatures des payloads et logique de retry
Vous avez construit une API et devez maintenant notifier d'autres services lorsque des événements se produisent. Le polling est inefficace, vous vous tournez donc vers les webhooks. Mais vous découvrez vite que les webhooks peuvent échouer, être usurpés ou provoquer des traitements en double. Cet article explique comment sécuriser les webhooks avec des payloads signés et mettre en place une logique de retry robuste.
Que sont les webhooks ?
Les webhooks sont des callbacks HTTP définis par l'utilisateur. Lorsqu'un événement se produit dans votre système (par exemple, un paiement réussi), vous envoyez une requête HTTP POST à une URL fournie par l'abonné. Le payload contient généralement les données de l'événement au format JSON ou form.
Les webhooks permettent une communication en temps réel entre services sans polling constant. Ils sont largement utilisés par les passerelles de paiement, les outils CI/CD et les plateformes SaaS.
Pourquoi sécuriser les webhooks ?
Sans sécurité, quiconque connaît l'URL du webhook peut envoyer de fausses requêtes, entraînant une corruption des données ou une fraude. De plus, des attaquants pourraient rejouer d'anciennes requêtes. Signer les payloads avec un secret partagé garantit l'authenticité et l'intégrité.
Signer les payloads avec HMAC
HMAC (Hash-based Message Authentication Code) est une méthode courante pour signer les webhooks. L'émetteur et le récepteur partagent une clé secrète. L'émetteur calcule un HMAC du payload à l'aide du secret et l'inclut dans un en-tête (par exemple, X-Signature). Le récepteur recalcule le HMAC et le compare.
Étape par étape : envoyer un webhook signé
- Créez le payload (JSON).
- Calculez le HMAC-SHA256 du payload en utilisant le secret.
- Incluez le digest hexadécimal dans un en-tête, par exemple
X-Signature: sha256=.... - Envoyez la requête POST.
// Exemple Node.js avec crypto
const crypto = require('crypto');
const secret = 'votre_secret_partage';
const payload = JSON.stringify({ event: 'payment.success', amount: 100 });
const signature = crypto.createHmac('sha256', secret).update(payload).digest('hex');
// Envoyez un POST avec l'en-tête X-Signature: sha256=signature
Vérifier la signature
Du côté du récepteur, extrayez l'en-tête de signature, calculez le HMAC du corps brut de la requête et comparez à l'aide d'une comparaison à temps constant pour éviter les attaques temporelles.
// Exemple Express.js
app.post('/webhook', (req, res) => {
const signature = req.headers['x-signature'];
const computed = crypto.createHmac('sha256', secret).update(req.rawBody).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(computed))) {
return res.status(401).send('Signature invalide');
}
// Traiter le payload
res.status(200).send('OK');
});
Remarque : assurez-vous d'utiliser le corps brut, et non le JSON analysé, car l'analyse peut modifier le payload.
Prévenir les attaques par rejeu
Incluez un horodatage dans le payload signé et rejetez les requêtes plus anciennes que quelques minutes. Incluez également un ID d'événement unique et stockez les ID traités pour éviter les doublons.
Logique de retry pour une livraison fiable
Les points de terminaison des webhooks peuvent être indisponibles ou lents. Mettez en place des retries avec un backoff exponentiel et du jitter. Définissez un nombre maximal de tentatives (par exemple, 5) et un timeout pour chaque requête.
Stratégie de retry
- Backoff exponentiel : Attendez 1s, 2s, 4s, 8s, etc.
- Jitter : Ajoutez de l'aléatoire pour éviter l'effet de troupeau.
- Idempotence : Utilisez une clé d'idempotence pour que les livraisons en double ne provoquent pas d'actions en double.
Exemple de calendrier de retry : 1er retry après 5s, 2e après 30s, 3e après 2min, 4e après 10min, 5e après 1h.
Gérer les échecs
Si tous les retries échouent, enregistrez l'échec et notifiez l'abonné (par exemple, par e-mail ou tableau de bord). Offrez un moyen de renvoyer manuellement les webhooks.
Comparaison : payloads signés vs authentification basique
| Méthode | Sécurité | Complexité | Protection contre le rejeu |
|---|---|---|---|
| Payload signé HMAC | Élevée | Modérée | Oui (avec horodatage) |
| Authentification basique | Faible | Faible | Non |
| Clé API dans l'en-tête | Moyenne | Faible | Non |
Bonnes pratiques
- Utilisez toujours HTTPS pour les points de terminaison des webhooks.
- Faites tourner les secrets périodiquement.
- Validez le schéma du payload avant de le traiter.
- Répondez rapidement (dans les 2xx) et traitez de manière asynchrone.
- Surveillez les métriques de livraison des webhooks (taux de réussite, latence).
FAQ
Comment vérifier une signature de webhook ?
Calculez le HMAC du corps brut de la requête en utilisant votre secret et comparez-le à l'en-tête de signature à l'aide d'une comparaison à temps constant.
Que faire si un webhook échoue ?
Mettez en place des retries avec un backoff exponentiel et du jitter. Si tous les retries échouent, enregistrez l'échec et proposez une option de renvoi manuel.
Puis-je utiliser des webhooks sans signature ?
Ce n'est pas recommandé. Sans signature, vous ne pouvez pas vérifier l'émetteur, ce qui rend votre système vulnérable à l'usurpation et aux attaques par rejeu.
Lorsque vous testez des payloads de webhooks, vous pouvez avoir besoin d'inspecter les réponses JSON. Utilisez notre JSON Formatter pour formater et valider rapidement les données JSON.