Webhooks expliqués : signatures et retries
Pourquoi les webhooks ont besoin de signatures et de retries
Vous avez créé un endpoint pour recevoir des webhooks d'un fournisseur de paiement ou d'une plateforme SaaS. Tout fonctionne en test, mais en production vous faites face à deux problèmes : comment savoir que la requête provient réellement du fournisseur, et que se passe-t-il si votre serveur est indisponible ou lent ? Sans signatures, n'importe qui peut forger une requête. Sans retries, une simple coupure réseau peut faire perdre un événement définitivement.
Ce guide explique comment implémenter correctement les deux. Nous aborderons la signature HMAC, les étapes de vérification, les stratégies de retry et l'idempotence — les trois piliers d'une gestion fiable des webhooks.
Comment fonctionnent les signatures de webhooks
Une signature de webhook est un hash cryptographique qui prouve que le payload a été créé par quelqu'un qui connaît un secret partagé. La plupart des fournisseurs utilisent HMAC-SHA256. L'émetteur calcule la signature sur le corps brut de la requête (parfois avec un timestamp) et l'inclut dans un en-tête. Le récepteur la recalcule et la compare.
Voici un flux typique :
- Le fournisseur et le consommateur partagent une clé secrète (configurée dans le tableau de bord du fournisseur).
- Le fournisseur crée le payload, calcule
HMAC-SHA256(secret, payload)et l'envoie dans un en-tête commeX-Signature. - Le consommateur reçoit la requête, lit le corps brut, recalcule le HMAC et le compare à l'aide d'une fonction à temps constant.
- Si les signatures correspondent, il traite l'événement ; sinon, il rejette avec un code 401.
Important : Utilisez toujours le corps brut de la requête tel que reçu, et non une version parsée puis re-sérialisée. La sérialisation JSON peut modifier les espaces et l'ordre des clés, ce qui casse la signature.
Ajouter des timestamps pour prévenir les attaques par rejeu
Un payload signé seul peut être rejoué par un attaquant qui capture une requête valide. Pour atténuer ce risque, incluez un timestamp dans les données signées et rejetez les requêtes plus anciennes que quelques minutes. Par exemple :
signed_payload = timestamp + "." + raw_body
signature = HMAC-SHA256(secret, signed_payload)
Le consommateur vérifie que le timestamp est dans une fenêtre acceptable (par exemple 5 minutes) et que la signature correspond.
Implémenter la vérification de signature
Regardons un exemple concret en Python avec Flask. Nous allons vérifier une signature envoyée dans l'en-tête X-Signature.
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(401)
raw_body = request.get_data()
expected = hmac.new(SECRET, raw_body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, signature):
abort(401)
# Traiter l'événement
event = request.json
# ... gérer l'événement ...
return '', 200
Points clés :
- Utilisez
hmac.compare_digest(ou équivalent) pour une comparaison à temps constant afin d'éviter les attaques temporelles. - Accédez au corps brut via
request.get_data()avant tout parsing. - Retournez 200 rapidement ; effectuez les traitements lourds de manière asynchrone.
Concevoir une stratégie de retry
Même avec des signatures, les webhooks peuvent échouer : votre serveur peut être indisponible, renvoyer une erreur 500 ou expirer. Les fournisseurs réessaient généralement avec un backoff exponentiel. En tant que consommateur, vous devez également gérer les retries avec élégance.
Voici ce qu'il faut considérer :
- Idempotence : Chaque événement doit avoir un ID unique. Stockez les IDs traités pour éviter les traitements en double lors des retries.
- Répondre rapidement : Accusez réception avec un statut 2xx en quelques secondes. Mettez l'événement en file d'attente pour un traitement en arrière-plan.
- Logique de retry de votre côté : Si vous appelez des services en aval, implémentez des retries avec backoff et des disjoncteurs (circuit breakers).
La plupart des fournisseurs réessaient sur les réponses non-2xx ou les timeouts, souvent jusqu'à 24 heures avec des intervalles croissants. Consultez la documentation de votre fournisseur pour les détails.
Exemple : gestionnaire d'événements idempotent
Utilisez une table de base de données pour suivre les IDs d'événements traités. Voici un modèle simplifié :
def handle_event(event_id, payload):
if db.exists('processed_events', event_id):
return # déjà traité
# traiter le payload
db.insert('processed_events', event_id)
# ... travail réel ...
Pour un haut débit, utilisez une contrainte d'unicité sur l'ID d'événement et interceptez les erreurs de clé dupliquée.
Comparaison : webhooks signés vs non signés
| Aspect | Payloads signés | Payloads non signés |
|---|---|---|
| Sécurité | Vérifie l'authenticité de l'émetteur | N'importe qui peut forger des requêtes |
| Protection contre le rejeu | Possible avec des timestamps | Aucune |
| Effort d'implémentation | Modéré (HMAC + vérification du timestamp) | Faible |
| Recommandé pour | Tous les webhooks en production | Tests internes uniquement |
Pièges courants et bonnes pratiques
- Ne parsez pas avant de vérifier : Vérifiez toujours la signature sur le corps brut en premier.
- Utilisez HTTPS : Les signatures protègent l'intégrité, mais HTTPS protège la confidentialité.
- Faites tourner les secrets : Supportez plusieurs secrets actifs pendant la rotation pour éviter les interruptions.
- Journalisez les échecs : Enregistrez les échecs de correspondance de signature et les retries pour le débogage.
- Testez avec des payloads réels : Utilisez des outils comme webhook.site ou la CLI de votre fournisseur pour simuler des événements.
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 que la requête provient d'une source fiable et n'a pas été altérée.
Comment gérer les retries de webhooks sans dupliquer les actions ?
Rendez votre gestionnaire idempotent en suivant les IDs d'événements. Avant de traiter, vérifiez si l'ID a déjà été traité ; si oui, retournez un succès sans répéter le travail.
Puis-je utiliser des webhooks sans signatures ?
Ce n'est pas recommandé pour la production. Sans signatures, vous ne pouvez pas distinguer les requêtes légitimes des requêtes forgées. Vérifiez toujours les signatures lorsqu'elles sont disponibles.
Lors du débogage de payloads de webhooks ou de l'analyse de logs serveur, le JSON Formatter peut vous aider à inspecter et valider rapidement les structures JSON.