Les pubs vous déplaisent ? Aller Sans pub Auj.

HMAC Comment les webhooks savent que vous ne mentez pas

Publié le

Chaque fois que GitHub, Stripe ou Shopify envoie un webhook vers votre serveur, ils signent le payload avec HMAC. Voici exactement comment cela fonctionne — et comment le vérifier dans votre propre code.

HMAC : Comment les webhooks savent que vous ne mentez pas 1
ANNONCE · Supprimer ?

Vous recevez une requête POST affirmant provenir de Stripe. Le corps de la requête indique qu'un paiement a réussi. Vous traitez l'ordre, vous envoyez les produits — et trois jours plus tard, vous découvrez que la requête a été fausse. Ouch.

C'est pourquoi chaque fournisseur sérieux de webhooks signe ses corps de requête à l'aide de HMAC. Si vous comprenez l'HMAC, vous comprenez pourquoi GitHub, Stripe, Shopify, Twilio et presque tous les API modernes l'utilisent — et vous saurez exactement comment vérifier ces signatures dans votre propre code serveur.

Ce que l'HMAC est réellement

HMAC signifie Code d'authentification basé sur le hachage. Il répond à une seule question : « L'individu qui m'a envoyé ce message connaissait-elle le secret partagé ? »

Il fonctionne en exécutant une fonction de hachage cryptographique — généralement SHA-256 — sur la combinaison d'une clé secrète et du corps de la requête. Le résultat est une chaîne de caractères de longueur fixe qui :

  • Change complètement si un seul octet du message change
  • Ne peut pas être produit sans connaître la clé secrète
  • Ne peut pas être inversé pour révéler la clé ou le message original

La formule est compacte : HMAC(key, message) = H((key ⊕ opad) || H((key ⊕ ipad) || message)). Vous n'avez pas besoin de mémoriser les internals — chaque langage dispose d'une implémentation dans sa bibliothèque standard — mais il est utile de comprendre l'intention : la clé est mélangée dans le hachage deux fois, de manière différente, afin d'éviter une classe d'attaques appelée attaques de prolongation de longueur.

Comment un fournisseur de webhooks l'utilise

Lorsque vous inscrivez un point d'entrée de webhook, le fournisseur vous donne un secret de signature — une chaîne aléatoire que vous et eux seul connaissez. Lorsqu'un événement se déclenche :

  1. Le fournisseur sérialise le corps de l'événement en JSON (ou une chaîne canonique).
  2. Il calcule HMAC-SHA256(secret, payload).
  3. Il envoie la requête avec la signature dans une en-tête — X-Hub-Signature-256 pour GitHub, Stripe-Signature pour Stripe, et ainsi de suite.

À votre côté, vous effectuez le même calcul sur le corps brut de la requête et vous le comparez. Si les résultats sont identiques, le corps est authentique. Si ce n'est pas le cas, vous le rejetez.

Vérification dans le code

Voici ce que la vérification ressemble dans les langages les plus courants. Le modèle est identique dans tous les cas : calculer, comparer avec une fonction de comparaison à temps constant.

Node.js

const crypto = require('crypto');

function verifyWebhook(rawBody, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)          // rawBody must be a Buffer or string — NOT parsed JSON
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

Python

import hmac
import hashlib

def verify_webhook(raw_body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        raw_body,
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(expected, signature)

PHP

function verifyWebhook(string $rawBody, string $signature, string $secret): bool {
    $expected = hash_hmac('sha256', $rawBody, $secret);
    return hash_equals($expected, $signature);
}

Le détail critique : utiliser le corps brut

L'erreur d'implémentation la plus courante est de passer un payload analysé et re-sérialisé à la fonction HMAC au lieu des octets bruts d'origine. L'ordre des clés, les espaces en blanc et les échappements Unicode influencent tous le hachage. Le fournisseur a signé les octets exacts qu'il a envoyés sur le réseau — vous devez hacher exactement ces mêmes octets.

Dans Express (Node.js), cela signifie configurer le parser de corps pour préserver le tampon brut :

app.use('/webhooks', express.raw({ type: 'application/json' }));

Dans Django, utilisez request.body plutôt que request.data. Dans Flask, utilisez request.get_data().

Pourquoi pas simplement un hachage simple ?

Un hachage simple SHA-256 du payload prouve rien — n'importe qui peut calculer SHA256(payload) sans secret. La clé secrète de l'HMAC est ce qui le rend un authentification code, et non simplement un hachage de contrôle. Il répond à la question « qui m'a envoyé ce message » plutôt qu'à la question « a-t-il été corrompu lors du transit ».

Pourquoi pas des signatures asymétriques (comme RSA) ?

RSA et ECDSA permettent au receveur de vérifier une signature sans connaître la clé privée — c'est une fonction utile pour les transmissions publiques (comme la signature de code). Pour les webhooks, il n'y a que deux parties qui ont besoin de vérifier la signature : vous et le fournisseur. Un secret partagé est plus simple, plus rapide, et tout aussi sécurisé dans ce modèle. Certains fournisseurs (Svix, Clerk) proposent des signatures asymétriques pour les cas où vous ne pouvez pas stocker de secret côté serveur.

Les attaques de répétition — et comment les éviter

Un signature HMAC valide prouve l'authenticité mais pas la fraîcheur. Un attaquant qui capte une requête authentique peut la répéter plus tard. Stripe combat cela en incluant une date dans la Stripe-Signature en-tête et en hachant la date avec le corps de la requête. Sur votre côté, vous rejeterez toute requête dont la date est plus de cinq minutes d'âge.

Si vous construisez votre propre système de webhook, faites de même : incluez un nonce croissant ou un timestamp Unix dans le message signé, et rejeté les requêtes périmées côté serveur.

La comparaison à temps constant n'est pas optionnelle

N'utilisez jamais une comparaison simple des signatures HMAC (===, ==). Une comparaison de chaîne qui court-circuite révèle des informations sur le nombre d'octets correspondants — un attaquant effectuant des milliers de requêtes peut reconstruire la signature attendue octet par octet. Utilisez toujours une comparaison à temps constant :

  • Node.js : crypto.timingSafeEqual()
  • Python : hmac.compare_digest()
  • PHP : hash_equals()
  • Go : hmac.Equal()
  • Ruby : ActiveSupport::SecurityUtils.secure_compare()

Résumé : un gestionnaire de webhook de production prêt à l'usage

Voici un exemple complet utilisant l'en-tête X-Hub-Signature-256 de GitHub dans Node.js :

const express = require('express');
const crypto = require('crypto');

const app = express();
const WEBHOOK_SECRET = process.env.GITHUB_WEBHOOK_SECRET;

// Keep the body as raw bytes — critical!
app.use('/github/webhook', express.raw({ type: 'application/json' }));

app.post('/github/webhook', (req, res) => {
  const sigHeader = req.headers['x-hub-signature-256'];
  if (!sigHeader) return res.status(401).send('Missing signature');

  const sig = sigHeader.replace('sha256=', '');
  const expected = crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(req.body)
    .digest('hex');

  if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig))) {
    return res.status(401).send('Invalid signature');
  }

  const event = JSON.parse(req.body);
  // Safe to process the event now
  console.log('Event type:', req.headers['x-github-event']);

  res.sendStatus(200);
});

app.listen(3000);

Référence rapide : qui utilise quoi

FournisseurEn-têteAlgorithmeDate dans la signature ?
GitHubX-Hub-Signature-256HMAC-SHA256Non
StripeStripe-SignatureHMAC-SHA256Oui
ShopifyX-Shopify-Hmac-Sha256HMAC-SHA256Non
TwilioX-Twilio-SignatureHMAC-SHA1Non
MouX-Slack-SignatureHMAC-SHA256Oui
PaddlePaddle-SignatureHMAC-SHA256Oui
Envie d'une expérience sans pub ? Passez à la version sans pub

Installez nos extensions

Ajoutez des outils IO à votre navigateur préféré pour un accès instantané et une recherche plus rapide

Sur Extension Chrome Sur Extension de bord Sur Extension Firefox Sur Extension de l'opéra

Le Tableau de Bord Est Arrivé !

Tableau de Bord est une façon amusante de suivre vos jeux, toutes les données sont stockées dans votre navigateur. D'autres fonctionnalités arrivent bientôt !

ANNONCE · Supprimer ?
ANNONCE · Supprimer ?
ANNONCE · Supprimer ?

Coin des nouvelles avec points forts techniques

Impliquez-vous

Aidez-nous à continuer à fournir des outils gratuits et précieux

Offre-moi un café
ANNONCE · Supprimer ?