Webhooks : signatures et nouvelles tentatives

Backend2026-10-09TryQuickToolBox

Vous venez d'intégrer une passerelle de paiement et vous devez maintenant mettre à jour votre base de données lorsqu'un paiement réussit. Interroger l'API toutes les quelques secondes est inefficace et lent. Les webhooks résolvent ce problème en poussant les événements vers votre serveur dès qu'ils se produisent. Mais sans sécurité et fiabilité appropriées, les webhooks peuvent devenir une source de bugs et de vulnérabilités. Cet article explique comment implémenter correctement les webhooks, en se concentrant sur les payloads signés et les retries.

Que sont les webhooks ?

Un webhook est un callback HTTP : un événement se produit dans un service, et ce service envoie une requête HTTP POST vers une URL que vous fournissez. Le payload contient généralement des données JSON décrivant l'événement. Par exemple, lorsqu'un client effectue un paiement, Stripe envoie un événement charge.succeeded à votre endpoint.

Les webhooks sont basés sur le push, ce qui réduit la latence et la charge serveur par rapport au polling. Cependant, ils introduisent des défis : comment savoir si la requête est authentique ? Et si votre serveur est indisponible au moment où l'événement se déclenche ?

Pourquoi les payloads signés sont importants

N'importe qui peut envoyer une requête HTTP POST à votre endpoint webhook. Sans vérification, un attaquant pourrait falsifier des événements, entraînant des actions non autorisées comme marquer une commande comme payée. Les payloads signés résolvent ce problème en incluant une signature cryptographique que seul l'émetteur et vous pouvez générer.

La méthode la plus courante est HMAC (Hash-based Message Authentication Code) avec un secret partagé. L'émetteur calcule un hash du payload à l'aide du secret et l'inclut dans un en-tête. Vous recalculez le hash et comparez. S'ils correspondent, la requête est authentique.

Comment vérifier un payload signé

Voici une approche étape par étape avec Node.js et Express :

  1. Récupérez le corps brut de la requête sous forme de chaîne. Ne le parsez pas en JSON avant la vérification, car le parsing peut altérer la séquence d'octets.
  2. Extrayez la signature de l'en-tête (par exemple, X-Signature).
  3. Calculez le HMAC en utilisant votre secret et le corps brut.
  4. Comparez la signature calculée avec celle reçue en utilisant une comparaison à temps constant pour éviter les attaques temporelles.
const crypto = require('crypto');

function verifySignature(rawBody, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
  const signature = req.headers['x-signature'];
  if (!verifySignature(req.body, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  const event = JSON.parse(req.body);
  // Process event
  res.status(200).send('OK');
});

Utilisez toujours un secret fort et faites-le tourner périodiquement. Ne l'exposez jamais dans du code côté client.

Implémenter des retries avec backoff

Les webhooks sont livrés via Internet, donc des échecs se produisent. Votre serveur peut être indisponible, ou le réseau peut avoir un hoquet. Un système de webhooks robuste réessaie les livraisons échouées avec un backoff exponentiel.

Généralement, l'émetteur réessaie s'il ne reçoit pas de réponse 2xx dans un délai imparti (par exemple, 5 secondes). Le calendrier de retry peut être : immédiat, puis après 1 minute, 5 minutes, 30 minutes, 2 heures, etc., jusqu'à un nombre maximum de tentatives (par exemple, 5).

En tant que récepteur, vous devez répondre rapidement pour éviter les timeouts. Accusez réception avec un 200 OK et traitez l'événement de manière asynchrone.

Gérer l'idempotence

Parce que les retries peuvent provoquer des livraisons en double, le traitement de vos événements doit être idempotent. Incluez un ID d'événement unique dans le payload et stockez les IDs traités. Avant de traiter, vérifiez si l'ID a déjà été géré.

async function processEvent(event) {
  const { id, type, data } = event;
  if (await db.processedEvents.findOne({ id })) {
    return; // Already processed
  }
  await db.processedEvents.insertOne({ id });
  // Handle event based on type
}

Utilisez une transaction de base de données pour vous assurer que l'événement n'est marqué comme traité qu'après un traitement réussi.

Bonnes pratiques pour la sécurité des webhooks

Comparaison : Polling vs Webhooks

AspectPollingWebhooks
LatenceÉlevée (basée sur l'intervalle)Faible (quasi temps réel)
Charge serveurÉlevée (requêtes constantes)Faible (uniquement sur événements)
ComplexitéSimple à implémenterNécessite sécurité et retries
FiabilitéDépend de la fréquence de pollingDépend de la logique de retry

FAQ

Comment tester les webhooks en local ?

Utilisez un outil comme ngrok pour exposer votre serveur local sur Internet. Configurez le fournisseur de webhook pour envoyer les événements vers votre URL ngrok.

Que faire si mon serveur est indisponible lors de l'envoi d'un webhook ?

L'émetteur doit réessayer avec un backoff. Assurez-vous que votre serveur est hautement disponible et répond rapidement pour éviter les timeouts.

Puis-je utiliser des signatures asymétriques au lieu de HMAC ?

Oui, certains fournisseurs utilisent des signatures RSA ou ECDSA. Vous vérifiez avec leur clé publique. HMAC est plus simple mais nécessite un secret partagé.

Lors du débogage de payloads de webhooks, il est utile de formater le JSON pour le rendre lisible. Essayez notre JSON Formatter pour embellir et valider instantanément les payloads de webhooks.