Webhooks expliqués : signatures et tentatives

Backend2026-10-18TryQuickToolBox

Vous avez construit une API et devez maintenant notifier d'autres services lorsque des événements se produisent. Vous mettez en place un point de terminaison webhook, mais vous découvrez rapidement que n'importe qui peut envoyer de faux payloads, et que lorsque votre serveur est hors ligne, les événements sont perdus à jamais. C'est un piège courant : les webhooks sont simples en concept mais nécessitent une conception minutieuse pour la sécurité et la fiabilité.

Dans ce guide, nous allons parcourir l'essentiel des webhooks : comment signer les payloads pour empêcher toute altération, comment vérifier les signatures côté récepteur, et comment implémenter une logique de nouvelle tentative avec backoff exponentiel pour gérer les échecs. Nous aborderons également l'idempotence pour éviter le traitement en double. À la fin, vous disposerez d'un système de webhooks robuste et fiable.

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 traité), il envoie une requête HTTP POST à une URL que vous spécifiez, avec un payload contenant les détails de l'événement. Cela permet une communication en temps réel entre les services sans interrogation répétée (polling).

Cependant, les webhooks introduisent deux défis :

Sécuriser les webhooks avec des payloads signés

Pour empêcher les requêtes falsifiées, l'expéditeur doit signer le payload à l'aide d'un secret partagé. Le récepteur peut ensuite vérifier la signature pour authentifier l'expéditeur. La méthode la plus courante est HMAC (Hash-based Message Authentication Code) avec SHA-256.

Comment fonctionne la signature HMAC

  1. L'expéditeur et le récepteur partagent une clé secrète (idéalement une chaîne longue et aléatoire).
  2. Pour chaque webhook, l'expéditeur calcule un HMAC du corps de la requête à l'aide du secret et l'inclut dans un en-tête (par exemple, X-Signature).
  3. Le récepteur calcule le même HMAC sur le corps reçu et le compare à la valeur de l'en-tête. S'ils correspondent, la requête est authentique.

Il est crucial de signer le corps brut de la requête, et non une version analysée, pour éviter les divergences dues aux différences de sérialisation JSON.

Exemple : signer un payload en Node.js

const crypto = require('crypto');

function signPayload(payload, secret) {
  return crypto.createHmac('sha256', secret).update(payload).digest('hex');
}

// Côté expéditeur :
const payload = JSON.stringify({ event: 'payment.success', amount: 100 });
const signature = signPayload(payload, 'your-secret-key');
// Inclure la signature dans l'en-tête : X-Signature: signature

Vérifier la signature côté récepteur

Du côté récepteur, vous devez vérifier la signature avant de traiter le payload. Utilisez une comparaison à temps constant pour éviter les attaques temporelles.

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));
}

// Dans votre point de terminaison webhook :
app.post('/webhook', (req, res) => {
  const signature = req.headers['x-signature'];
  const payload = req.rawBody; // corps brut sous forme de chaîne
  if (!verifySignature(payload, signature, 'your-secret-key')) {
    return res.status(401).send('Invalid signature');
  }
  // Traiter l'événement
  res.status(200).send('OK');
});

Remarque : Utilisez toujours HTTPS pour éviter les attaques de type man-in-the-middle. Envisagez également d'inclure un horodatage dans le payload signé pour prévenir les attaques par rejeu.

Implémenter des tentatives avec backoff exponentiel

Même avec un récepteur fiable, des problèmes réseau ou des pannes temporaires peuvent entraîner l'échec de la livraison des webhooks. Un expéditeur robuste doit réessayer les livraisons échouées. Cependant, des tentatives naïves peuvent submerger le récepteur ou provoquer un traitement en double. La solution : un backoff exponentiel avec jitter et une limite maximale de tentatives.

Stratégie de nouvelle tentative

Exemple : logique de nouvelle tentative en Node.js

async function deliverWebhook(url, payload, secret, maxRetries = 5) {
  const signature = signPayload(payload, secret);
  let attempt = 0;
  while (attempt < maxRetries) {
    try {
      const response = await fetch(url, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'X-Signature': signature },
        body: payload,
      });
      if (response.ok) return;
      throw new Error(`HTTP ${response.status}`);
    } catch (error) {
      attempt++;
      if (attempt >= maxRetries) throw error;
      const delay = Math.min(1000 * 2 ** attempt + Math.random() * 1000, 30000);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}

Cette fonction réessaie jusqu'à 5 fois avec un backoff exponentiel et un jitter, en plafonnant le délai à 30 secondes. Ajustez les paramètres selon vos besoins.

Garantir l'idempotence

Les tentatives peuvent entraîner des livraisons en double, votre gestionnaire de webhook doit donc être idempotent. Incluez un ID d'événement unique dans le payload et demandez au récepteur de suivre les ID traités pour éviter un double traitement.

// Côté récepteur :
const processedEvents = new Set();

app.post('/webhook', (req, res) => {
  const { id } = req.body;
  if (processedEvents.has(id)) {
    return res.status(200).send('Already processed');
  }
  // Traiter l'événement
  processedEvents.add(id);
  res.status(200).send('OK');
});

En production, utilisez un stockage persistant (par exemple, Redis ou une base de données) pour suivre les ID traités à travers les redémarrages.

Comparaison : webhooks signés vs non signés

Aspect Non signé Signé (HMAC)
Sécurité N'importe qui peut falsifier des requêtes Seul le détenteur du secret peut créer une signature valide
Intégrité Le payload peut être altéré Toute altération invalide la signature
Complexité Faible Modérée (gestion des clés, vérification)
Cas d'utilisation Réseaux internes de confiance Tout webhook public

FAQ

Qu'est-ce qu'une signature de webhook ?

Une signature de webhook est un hachage cryptographique (généralement HMAC) du payload, créé avec un secret partagé. Elle permet au récepteur de vérifier que la requête provient de l'expéditeur attendu et que le payload n'a pas été altéré.

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

Il n'y a pas de nombre fixe, mais la pratique courante est de 3 à 5 tentatives avec un backoff exponentiel. Ensuite, marquez la livraison comme échouée et alertez votre équipe. Tenez compte de la criticité de l'événement : les notifications de paiement peuvent justifier plus de tentatives que les mises à jour non critiques.

Puis-je utiliser des webhooks sans HTTPS ?

Vous ne devriez jamais utiliser de webhooks sur HTTP simple en production. HTTPS chiffre le payload en transit, empêchant l'écoute clandestine et les attaques de type man-in-the-middle. Même avec des signatures, HTTP expose des données sensibles.

Prêt à tester vos payloads de webhook ? Utilisez notre JSON Formatter pour embellir et valider les payloads JSON avant de les envoyer ou de les déboguer.