Webhooks expliqués : payloads signés et retries

Backend2026-10-14TryQuickToolBox

Pourquoi les webhooks sont délicats

Les webhooks sont une idée simple : au lieu d'interroger une API pour détecter des changements, le fournisseur pousse les événements vers votre endpoint via HTTP POST. Mais en pratique, les webhooks introduisent un ensemble de défis qui piègent de nombreux développeurs : vérifier que la requête provient bien du fournisseur, gérer les livraisons en double et faire face aux pannes réseau. Si vous ignorez ces aspects, vous risquez des failles de sécurité, une corruption des données et des utilisateurs mécontents.

Cet article passe en revue les deux aspects les plus critiques des webhooks en production : les payloads signés et les retries. Nous verrons comment vérifier les signatures, comment implémenter des retries avec backoff exponentiel et comment rendre votre traitement idempotent.

Payloads signés : prouver l'authenticité

N'importe qui peut envoyer un HTTP POST vers votre endpoint. Pour s'assurer que la requête provient bien de votre fournisseur, celui-ci signe le payload avec une clé secrète et envoie la signature dans un en-tête. Votre travail consiste à recalculer la signature et à la comparer.

La plupart des fournisseurs utilisent HMAC-SHA256. La signature est généralement calculée sur le corps brut de la requête (parfois avec un timestamp) et encodée en hexadécimal. Par exemple, Stripe utilise un en-tête comme Stripe-Signature contenant un timestamp et une signature.

Comment fonctionne HMAC

HMAC (Hash-based Message Authentication Code) combine une clé secrète avec une fonction de hachage. Avec la même clé et le même message, le résultat est déterministe. Sans la clé, un attaquant ne peut pas forger une signature valide.

Voici un exemple minimal en Node.js :

const crypto = require('crypto');

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

En Python, c'est tout aussi simple :

import hmac
import hashlib

def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

Bonnes pratiques pour la vérification de signature

Gérer les timestamps et les attaques par rejeu

De nombreux fournisseurs incluent un timestamp dans l'en-tête de signature. Par exemple, l'en-tête peut être t=1614556800,v1=abc123.... Vous devez :

  1. Extraire le timestamp et la signature.
  2. Vérifier que le timestamp est dans une fenêtre acceptable (par ex., 5 minutes).
  3. Recalculer la signature en utilisant le timestamp et le payload.
  4. Comparer les signatures de manière sécurisée.

Cela empêche un attaquant de rejouer une ancienne requête valide.

Retries : gérer les échecs avec élégance

Les webhooks sont livrés sur l'internet public, donc les échecs sont inévitables. Votre endpoint peut être indisponible, lent ou renvoyer une erreur 500. Un fournisseur robuste retentera les livraisons échouées, mais vous devez aussi gérer les retries de votre côté si vous êtes le fournisseur.

Backoff exponentiel avec jitter

Lors des retries, évitez de marteler l'endpoint avec des tentatives immédiates. Utilisez un backoff exponentiel : attendez plus longtemps après chaque échec. Ajoutez du jitter (aléatoire) pour éviter les problèmes de thundering herd lorsque de nombreux webhooks échouent simultanément.

Un calendrier de retry typique pourrait ressembler à :

TentativeDélai (approx)
1Immédiat
230 secondes
35 minutes
430 minutes
52 heures

Après un nombre maximum de tentatives, marquez la livraison comme échouée et alertez.

Idempotence : la clé des retries sûrs

Comme les retries peuvent causer des livraisons en double, votre gestionnaire de webhook doit être idempotent. Cela signifie que traiter le même événement plusieurs fois doit avoir le même effet que le traiter une seule fois.

Comment atteindre l'idempotence :

Implémenter les retries côté fournisseur

Si vous construisez un système de webhooks, vous avez besoin d'un mécanisme de livraison fiable. Voici une approche simplifiée :

  1. Lorsqu'un événement se produit, mettez en file d'attente une tâche de livraison avec le payload et l'URL cible.
  2. Un worker récupère la tâche et envoie le HTTP POST avec un timeout (par ex., 10 secondes).
  3. Si le statut de la réponse n'est pas 2xx, planifiez un retry avec backoff exponentiel.
  4. Suivez les tentatives et abandonnez après un maximum (par ex., 5 tentatives).
  5. Journalisez les échecs et offrez un moyen de rejouer manuellement.

Utilisez une file persistante (par ex., Redis, RabbitMQ ou une table de base de données) pour que les retries survivent aux redémarrages.

Tout mettre ensemble : une checklist pour le récepteur de webhook

Lorsque vous construisez un récepteur de webhook, suivez cette checklist :

FAQ

Qu'est-ce qu'un payload de webhook signé ?

Un payload signé inclut une signature cryptographique (généralement HMAC-SHA256) générée avec un secret partagé. Le récepteur recalcule la signature pour vérifier que la requête provient de l'expéditeur attendu et n'a pas été altérée.

Pourquoi les webhooks ont-ils besoin de retries ?

Des problèmes réseau, une indisponibilité du serveur ou des erreurs temporaires peuvent faire échouer une livraison de webhook. Les retries avec backoff exponentiel garantissent que l'événement est finalement livré sans submerger le récepteur.

Comment éviter le traitement en double des webhooks ?

Utilisez l'idempotence : incluez un ID d'événement unique dans le payload, stockez les IDs traités et concevez vos opérations pour être idempotentes (par ex., des upserts). Ainsi, même si un webhook est livré plusieurs fois, l'effet est le même.

Si vous avez besoin d'inspecter ou de déboguer des payloads de webhook, le JSON Formatter peut vous aider à formater et valider rapidement des données JSON.