Webhooks expliqués : signatures et stratégies de relance
Vous avez construit une API qui envoie des webhooks aux endpoints de vos utilisateurs. Mais vous faites face aux problèmes classiques : comment vous assurer que le payload n'a pas été altéré ? Que se passe-t-il lorsque l'endpoint est indisponible ? Comment éviter le traitement des doublons ? Dans cet article, nous abordons ces défis de front avec des payloads signés et des stratégies de relance robustes.
Pourquoi les webhooks ont besoin de signatures
Les webhooks sont des callbacks HTTP qui notifient d'autres systèmes d'événements. Parce qu'ils sont envoyés sur l'internet public, ils sont vulnérables à l'interception et à l'usurpation. Sans signature, un acteur malveillant pourrait envoyer de faux webhooks à vos utilisateurs, déclenchant des actions non désirées. Signer les payloads avec un secret partagé garantit l'authenticité et l'intégrité.
Comment fonctionnent les payloads signés
L'approche la plus courante est HMAC (Hash-based Message Authentication Code) avec SHA-256. L'expéditeur et le récepteur partagent une clé secrète. L'expéditeur calcule une signature sur le payload (et parfois un timestamp) et l'inclut dans un en-tête. Le récepteur recalcule la signature et la compare à celle reçue.
Étape par étape : signer un payload
- Créez le payload : Sérialisez vos données d'événement en JSON. Assurez un formatage cohérent (par exemple, pas d'espaces supplémentaires) car la signature est calculée sur les octets exacts.
- Ajoutez un timestamp : Incluez un timestamp Unix dans le payload ou l'en-tête pour éviter les attaques par rejeu. Le récepteur peut rejeter les requêtes plus anciennes que quelques minutes.
- Calculez le HMAC : Utilisez votre clé secrète pour générer le HMAC-SHA256 de la chaîne du payload (ou timestamp + payload).
- Attachez la signature : Envoyez-la dans un en-tête comme
X-Signature.
Voici un exemple en Python :
import hmac
import hashlib
import json
import time
secret = b'your_shared_secret'
payload = {"event": "user.created", "data": {"id": 123}}
body = json.dumps(payload, separators=(',', ':'))
timestamp = str(int(time.time()))
signed_content = timestamp + '.' + body
signature = hmac.new(secret, signed_content.encode(), hashlib.sha256).hexdigest()
# Envoyer les en-têtes : X-Signature: signature, X-Timestamp: timestamp
Vérification de la signature
Côté récepteur, vous devez :
- Extraire le timestamp et la signature des en-têtes.
- Rejeter si le timestamp est trop ancien (par exemple, >5 minutes) pour éviter les attaques par rejeu.
- Recalculer la signature en utilisant la même méthode.
- Comparer en utilisant une fonction à temps constant pour éviter les attaques temporelles.
Exemple de vérification en Node.js :
const crypto = require('crypto');
function verifySignature(req, secret) {
const signature = req.headers['x-signature'];
const timestamp = req.headers['x-timestamp'];
if (!signature || !timestamp) return false;
if (Date.now() / 1000 - timestamp > 300) return false; // 5 min
const body = JSON.stringify(req.body);
const signed = timestamp + '.' + body;
const expected = crypto.createHmac('sha256', secret).update(signed).digest('hex');
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
Concevoir des stratégies de relance
Même avec des signatures, les webhooks peuvent échouer en raison de problèmes réseau, d'erreurs serveur ou de timeouts. Une stratégie de relance robuste garantit la livraison sans surcharger le récepteur.
Considérations clés
- Idempotence : Les récepteurs doivent gérer les événements en double avec élégance. Utilisez une clé d'idempotence (par exemple, l'ID de l'événement) pour dédupliquer.
- Backoff exponentiel : Relancez après des délais croissants (par exemple, 1s, 5s, 30s, 5m, 30m) pour laisser au récepteur le temps de récupérer.
- Nombre maximal de relances : Fixez une limite (par exemple, 5 tentatives) puis marquez le webhook comme échoué, en alertant éventuellement l'utilisateur.
- Timeouts : Définissez un timeout raisonnable (par exemple, 10 secondes) pour chaque tentative.
Implémentation des relances
La plupart des expéditeurs de webhooks utilisent un système de file d'attente (par exemple, Redis, RabbitMQ ou une base de données) pour planifier les relances. Voici un flux simplifié :
- Lors du déclenchement de l'événement, mettez en file d'attente une tâche de webhook avec le payload et l'URL cible.
- Un worker récupère la tâche et tente la livraison.
- Si le statut de la réponse est 2xx, marquez comme succès.
- Sinon, planifiez une relance avec backoff exponentiel, jusqu'au nombre maximal de tentatives.
- Après le nombre maximal de tentatives, marquez comme échec et notifiez.
Exemple de calendrier de relance :
| Tentative | Délai |
|---|---|
| 1 | Immédiat |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 heures |
Meilleures pratiques pour la sécurité des webhooks
- Utilisez HTTPS : Livrez toujours les webhooks via TLS pour éviter l'écoute clandestine.
- Faites tourner les secrets : Renouvelez périodiquement les secrets partagés et prenez en charge plusieurs secrets actifs pendant la rotation.
- Validez les URL : Assurez-vous que l'URL du webhook est en HTTPS et ne pointe pas vers des IP internes (pour éviter les SSRF).
- Journalisez et surveillez : Conservez des journaux des tentatives de livraison et des échecs pour le débogage et l'audit.
FAQ
Qu'est-ce qu'un payload de webhook signé ?
Un payload signé inclut une signature cryptographique (généralement HMAC) qui permet au récepteur de vérifier l'authenticité de l'expéditeur et que le payload n'a pas été altéré.
Combien de fois dois-je relancer un webhook échoué ?
Il n'y a pas de réponse unique, mais une approche courante est de 3 à 5 relances avec backoff exponentiel. Tenez compte de la criticité de l'événement et de la capacité du récepteur.
Puis-je utiliser JWT pour les signatures de webhooks ?
Oui, JWT peut être utilisé, mais c'est plus complexe. HMAC est plus simple et suffisant pour la plupart des cas d'usage. JWT est utile si vous devez inclure des revendications ou une expiration dans la signature elle-même.
Prêt à tester vos payloads de webhooks ? Utilisez notre JSON Formatter pour valider et embellir vos payloads JSON avant l'envoi.