Webhooks expliqués : charges signées et retentatives

Backend2026-10-24TryQuickToolBox

Vous avez construit une API et devez maintenant notifier d'autres services lorsque des événements se produisent. Vous avez mis en place un point de terminaison webhook, mais vous réalisez rapidement que livrer des requêtes HTTP de manière fiable est plus difficile qu'il n'y paraît. Les défaillances réseau, les acteurs malveillants et les événements en double peuvent causer le chaos. Ce guide explique comment implémenter des webhooks avec des charges signées et des retentatives afin de construire une intégration robuste.

Que sont les webhooks ?

Les webhooks sont des callbacks HTTP définis par l'utilisateur. Lorsqu'un événement se produit dans votre application, vous envoyez une requête HTTP POST à une URL fournie par l'abonné. Cela permet une communication en temps réel entre les services sans interrogation répétée (polling).

Par exemple, un processeur de paiement peut envoyer un webhook à votre application lorsqu'un paiement réussit. Votre application peut alors mettre à jour le statut de la commande immédiatement.

Pourquoi signer les charges des webhooks ?

Les points de terminaison webhook sont accessibles publiquement, donc n'importe qui peut envoyer de fausses requêtes. Sans vérification, votre application pourrait traiter des événements frauduleux. Signer les charges garantit que la requête provient de votre service et n'a pas été altérée.

La signature utilise généralement un secret partagé entre l'expéditeur et le destinataire. L'expéditeur calcule un code d'authentification de message basé sur un hachage (HMAC) sur la charge et l'inclut dans un en-tête. Le destinataire recalcule le HMAC et le compare.

Comment générer et vérifier les signatures

La plupart des implémentations utilisent HMAC-SHA256. Voici un exemple simple en Python :

import hmac
import hashlib
import json

def generate_signature(payload, secret):
    body = json.dumps(payload, separators=(',', ':')).encode('utf-8')
    signature = hmac.new(secret.encode('utf-8'), body, hashlib.sha256).hexdigest()
    return signature

def verify_signature(payload, signature, secret):
    expected = generate_signature(payload, secret)
    return hmac.compare_digest(expected, signature)

Lors de l'envoi du webhook, incluez la signature dans un en-tête comme X-Signature. Le destinataire extrait le corps brut, recalcule le HMAC et compare. Utilisez toujours une fonction de comparaison à temps constant pour éviter les attaques temporelles.

Implémenter des retentatives pour une livraison fiable

Les webhooks sont envoyés sur Internet, donc les échecs sont inévitables. Votre serveur peut être indisponible, le réseau peut être lent, ou l'abonné peut renvoyer une erreur. Les retentatives avec un backoff exponentiel sont essentielles.

Stratégie de retentative

Une approche courante consiste à réessayer avec des délais croissants : 1 minute, 5 minutes, 30 minutes, 2 heures, etc. Arrêtez après un nombre maximum de tentatives (par exemple, 10) ou une fenêtre de temps totale (par exemple, 24 heures).

Ne réessayez que dans des conditions spécifiques : erreurs réseau, réponses 5xx ou 429 (Too Many Requests). Ne réessayez pas sur les erreurs 4xx (sauf 429) car elles indiquent une erreur client qui ne sera pas corrigée par une retentative.

Exemple de logique de retentative

Voici un mécanisme de retentative simplifié en Python utilisant la bibliothèque requests :

import requests
import time
from requests.exceptions import RequestException

def send_webhook(url, payload, secret, max_retries=5):
    headers = {'Content-Type': 'application/json'}
    signature = generate_signature(payload, secret)
    headers['X-Signature'] = signature
    
    for attempt in range(max_retries):
        try:
            response = requests.post(url, json=payload, headers=headers, timeout=10)
            if response.status_code < 500 and response.status_code != 429:
                return response.status_code
        except RequestException:
            pass
        
        # Backoff exponentiel avec jitter
        delay = (2 ** attempt) + (random.random() * 0.1)
        time.sleep(delay)
    
    return None  # Échec après les retentatives

En production, vous stockeriez les tentatives échouées dans une file d'attente et les traiteriez de manière asynchrone. Des outils comme Celery ou des files d'attente cloud (AWS SQS, Google Cloud Tasks) peuvent vous aider.

Meilleures pratiques pour la sécurité des webhooks

Gérer les doublons et l'idempotence

Les retentatives peuvent entraîner des livraisons en double. Incluez un ID d'événement unique dans la charge et demandez au destinataire de suivre les ID traités. Si un ID d'événement a déjà été vu, ignorez-le.

Alternativement, concevez vos gestionnaires d'événements pour être idempotents. Par exemple, mettre à jour un enregistrement avec les mêmes données plusieurs fois devrait avoir le même effet que de le faire une seule fois.

Tester les webhooks

Utilisez des outils comme ngrok pour exposer votre serveur local et tester la livraison des webhooks. Simulez des échecs en renvoyant des codes d'erreur et vérifiez que les retentatives se produisent comme prévu.

FAQ

Qu'est-ce qu'une charge de webhook signée ?

Une charge signée inclut une signature cryptographique (généralement HMAC) qui permet au destinataire de vérifier que la requête provient d'une source fiable et n'a pas été altérée.

Combien de fois dois-je réessayer un webhook échoué ?

Cela dépend de vos besoins de fiabilité. Une pratique courante est de 5 à 10 retentatives avec un backoff exponentiel sur 24 heures. Permettez toujours aux abonnés de déclencher manuellement une retentative.

Puis-je utiliser des webhooks sans signature ?

Vous pouvez, mais c'est risqué. Sans signature, n'importe qui peut envoyer de faux événements. Signez toujours les webhooks en production pour prévenir la fraude.

Prêt à tester vos charges de webhook ? Utilisez notre JSON Formatter pour valider et embellir les charges JSON avant l'envoi.