Webhooks expliqués : signatures et tentatives
Vous venez d'intégrer un service tiers qui envoie des webhooks à votre endpoint. Tout fonctionne en test, mais en production, vous remarquez des événements manqués, des livraisons en double et parfois des problèmes de sécurité. Les webhooks sont simples en concept mais délicats en pratique, surtout pour vérifier l'authenticité et gérer les échecs.
Ce guide explique comment sécuriser les webhooks avec des signatures de payload et mettre en place des mécanismes de tentatives robustes pour construire des intégrations fiables.
Que sont les webhooks ?
Les webhooks sont des callbacks HTTP définis par l'utilisateur. Lorsqu'un événement se produit dans un système source (par exemple, un paiement est effectué), il envoie une requête HTTP POST à une URL que vous spécifiez, contenant des données sur l'événement. Cela permet une communication en temps réel entre services sans polling.
Mais les webhooks introduisent des défis : comment savoir que la requête provient bien de l'expéditeur attendu ? Et si votre endpoint est temporairement indisponible ? C'est là que les signatures de payload et les tentatives entrent en jeu.
Sécuriser les webhooks avec des signatures de payload
Une signature de payload utilise une signature cryptographique pour prouver que la requête provient de l'expéditeur et n'a pas été altérée. L'approche courante est HMAC (Hash-based Message Authentication Code) avec un secret partagé.
Comment fonctionnent les signatures HMAC
L'expéditeur calcule un HMAC sur le corps de la requête (et parfois des en-têtes comme l'horodatage) à l'aide d'une clé secrète. La signature est envoyée dans un en-tête (par exemple, X-Signature). Votre serveur recalcule le HMAC et le compare à la signature reçue. S'ils correspondent, la requête est authentique.
Voici un exemple minimal en Python avec Flask :
import hmac
import hashlib
from flask import Flask, request, abort
app = Flask(__name__)
SECRET = b'your_shared_secret'
@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('X-Signature')
if not signature:
abort(400, 'Missing signature')
body = request.get_data()
expected = hmac.new(SECRET, body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, signature):
abort(401, 'Invalid signature')
# Process the event
return '', 200
Points clés :
- Utilisez toujours une fonction de comparaison à temps constant (comme
hmac.compare_digest) pour éviter les attaques temporelles. - Incluez un horodatage dans les données signées pour éviter les attaques par rejeu ; rejetez les requêtes plus anciennes que quelques minutes.
- Gardez le secret sécurisé : ne l'exposez jamais côté client.
Mesures de sécurité supplémentaires
- HTTPS : Utilisez toujours HTTPS pour protéger le payload en transit.
- Liste blanche d'IP : Si le fournisseur publie des plages d'IP, limitez les requêtes à ces IP.
- Idempotence : Utilisez des ID d'événement pour dédupliquer, car les tentatives peuvent causer des doublons.
Implémenter des tentatives fiables
Les consommateurs de webhooks peuvent échouer : votre serveur peut être indisponible ou le traitement peut expirer. Les expéditeurs doivent réessayer les livraisons échouées avec un backoff exponentiel. En tant que consommateur, vous devez gérer les tentatives avec élégance.
Du côté de l'expéditeur (si vous construisez un système de webhooks)
- Retournez un 2xx rapidement : Accusez réception avant de traiter pour éviter les timeouts. Utilisez une file d'attente pour le travail asynchrone.
- Réessayez en cas d'échec : Si aucun 2xx n'est reçu dans un délai (par exemple, 5 secondes), réessayez avec des délais croissants (par exemple, 1s, 5s, 30s, 5m, 30m).
- Limitez les tentatives : Après un nombre maximum de tentatives (par exemple, 5), marquez la livraison comme échouée et alertez.
- Incluez un ID d'événement unique : Pour que les consommateurs puissent dédupliquer.
Du côté du consommateur (votre endpoint)
- Répondez vite : Retournez 2xx immédiatement et traitez de manière asynchrone si nécessaire.
- Soyez idempotent : Utilisez l'ID d'événement pour éviter de traiter deux fois le même événement.
- Gérez les doublons : Stockez les ID d'événements traités pendant une période raisonnable.
- Journalisez les échecs : Si le traitement échoue, retournez 5xx pour déclencher une nouvelle tentative, mais assurez-vous de ne pas rester bloqué dans une boucle.
Comparaison : signatures de payload vs authentification basique
| Approche | Sécurité | Complexité | Protection contre le rejeu |
|---|---|---|---|
| Signature HMAC | Élevée (intégrité + authenticité) | Modérée | Oui (avec horodatage) |
| Authentification basique | Faible (identifiants dans l'en-tête) | Faible | Non |
| Liste blanche d'IP | Moyenne (au niveau réseau) | Faible | Non |
Tester les webhooks localement
Pendant le développement, vous avez besoin d'une URL publique pour recevoir des webhooks. Des outils comme ngrok ou localtunnel peuvent exposer votre serveur local. Testez toujours la vérification de signature et le comportement des tentatives avec des échecs simulés.
Lors de l'inspection des payloads de webhooks, vous pouvez avoir besoin de formater le JSON pour le rendre lisible. Le JSON Formatter peut vous aider à embellir et valider le corps JSON.
FAQ
Qu'est-ce qu'un payload de webhook signé ?
Un payload signé inclut une signature cryptographique (souvent HMAC) qui permet au récepteur de vérifier l'authenticité et l'intégrité de la requête.
Combien de fois un webhook doit-il être réessayé ?
Il n'y a pas de nombre fixe ; les pratiques courantes vont de 3 à 10 tentatives avec un backoff exponentiel. L'essentiel est d'équilibrer la fiabilité avec l'utilisation des ressources et d'alerter en cas d'échecs persistants.
Puis-je utiliser des webhooks sans signatures ?
Ce n'est pas recommandé. Sans signatures, vous ne pouvez pas vérifier l'expéditeur, ce qui rend votre endpoint vulnérable à l'usurpation. Utilisez toujours des signatures en production.
Prêt à déboguer vos payloads de webhooks ? Essayez notre JSON Formatter pour valider et formater rapidement les données JSON.