HMAC — Comment les webhooks savent que vous n'êtes pas en train de mentir
Tout serveur peut envoyer une requête POST à votre point d'entrée de webhook. Les signatures HMAC permettent aux expéditeurs légitimes de prouver qu'ils ont rédigé le contenu — et vous permettent de le vérifier.
Chaque fois que Stripe charge une carte, il déclenche un webhook. Chaque fois que GitHub fusionne une demande de pull, il déclenche un webhook. Ces plateformes envoient des requêtes POST vers une URL que vous leur avez fournie — mais voici la vérité inquiétante : n'importe qui peut envoyer une requête POST vers cette même URL.
Alors, comment votre serveur sait que la requête provient vraiment de Stripe et non d'un attaquant qui a deviné l'URL de votre point d'entrée ? La réponse est l'HMAC — et une fois que vous l'avez comprise, vous comprendrez pourquoi c'est l'approche standard sur toutes les plateformes sérieuses d'API.
Le problème : n'importe qui peut envoyer un POST à votre point d'entrée
Les points d'entrée de webhook sont simplement des URLs. Elles sont publiquement accessibles (elles le doivent être, pour que l'expéditeur puisse les atteindre), et elles acceptent des requêtes POST. Il n'y a rien qui empêche un acteur malveillant de créer un payload faux et de l'envoyer à votre point d'entrée.
Imaginons que votre gestionnaire de webhook fait ceci lorsqu'il reçoit un événement :
if event["type"] == "payment.completed":
fulfill_order(event["data"]["order_id"])
Un attaquant qui connaît votre URL de point d'entrée pourrait envoyer un faux payment.completed événement avec n'importe quel ID de commande qu'il souhaite. Sans vérification, votre serveur accepterait des commandes qui n'avaient jamais été payées.
Vous avez besoin d'une manière de vérifier que le payload a été écrit par quelqu'un qui possède un secret que vous partagez — sans transmettre ce secret dans la requête.
Qu'est-ce que l'HMAC ?
HMAC signifie Code d'authentification basé sur le hachage. C'est une construction qui combine une fonction de hachage cryptographique (généralement SHA-256) avec une clé secrète pour produire une signature. Cette signature prouve deux choses :
- Authenticité — le message a été créé par quelqu'un qui possède la clé secrète
- Intégrité — le message n'a pas été modifié pendant le transit
La propriété clé de l'HMAC : vous ne pouvez pas produire une signature valide sans connaître la clé secrète. Et vous ne pouvez pas inverser la signature pour récupérer la clé. C'est une preuve unidirectionnelle.
L'HMAC par rapport à une simple hachage
Un simple hachage (comme SHA-256) du payload résout le problème d'intégrité, mais pas l'authenticité. Un attaquant qui intercepte un payload valide pourrait recalculer le hachage d'un payload modifié. L'HMAC mélange votre clé secrète dans chaque étape du processus de hachage, donc sans la clé, vous ne pouvez pas produire une signature correspondante même si vous connaissez l'algorithme de hachage utilisé.
Comment fonctionne l'HMAC des webhooks
Le processus comporte trois étapes : échange de clé, signature et vérification.
Étape 1 : Échange de clé (se produit une fois)
Lorsque vous configurez un webhook avec une plateforme comme Stripe ou GitHub, elles génèrent un secret de webhook et vous le montrent une seule fois. Vous le stockez côté serveur (jamais dans du code client ou dans des dépôts publics). C'est tout — la clé ne se transmet plus jamais sur le réseau.
Étape 2 : L'expéditeur signe le payload
Avant d'envoyer le webhook, la plateforme calcule une signature HMAC sur le corps brut de la requête en utilisant votre clé partagée :
signature = HMAC-SHA256(secret_key, request_body)
La signature est ensuite attachée à la requête, généralement dans une en-tête comme X-Hub-Signature-256 (GitHub) ou Stripe-Signature (Stripe). Le payload brut voyage dans le corps sans changement.
Étape 3 : Vous vérifiez à la réception
Lorsque votre serveur reçoit le webhook, vous recalculer la même HMAC à partir du corps brut et de votre clé stockée, puis vous comparez le résultat à la signature dans l'en-tête. Si elles correspondent, le payload est authentique et non modifié. Si elles ne correspondent pas, vous le rejetez.
expected = HMAC-SHA256(your_secret, raw_body)
if not constant_time_equal(expected, header_signature):
return 401
Avis comparaison en temps constant — nous reviendrons sur pourquoi cela compte.
Exemples concrets
Stripe
Stripe envoie une Stripe-Signature en-tête contenant une date et une ou plusieurs signatures :
Stripe-Signature: t=1679000000,v1=abc123...,v0=oldformat...
La signature est calculée sur timestamp.payload (concaténée avec un point). L'inclusion de la date permet à Stripe de se protéger contre les attaques de répétition — si un attaquant capte une requête valide et la réenvoie plus tard, vous pouvez la rejeter car la date est trop ancienne.
GitHub
GitHub envoie une X-Hub-Signature-256 en-tête au format sha256=<hex_digest>. La signature est un HMAC-SHA256 du corps brut en utilisant la clé de webhook que vous avez configurée dans les paramètres de votre dépôt.
Shopify
Shopify utilise une X-Shopify-Hmac-Sha256 en-tête avec une signature HMAC-SHA256 encodée en Base64 — même concept, différent encodage.
Vérification dans le code
Voici comment la vérification se présente dans trois langages courants. Le modèle est identique — seules les appels de bibliothèque varient.
Python
import hmac
import hashlib
def verify_webhook(secret: str, payload: bytes, signature_header: str) -> bool:
expected = hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
received = signature_header.removeprefix("sha256=")
return hmac.compare_digest(expected, received)
Node.js
const crypto = require('crypto');
function verifyWebhook(secret, rawBody, signatureHeader) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
const received = signatureHeader.replace('sha256=', '');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(received)
);
}
PHP
function verifyWebhook(string $secret, string $rawBody, string $signatureHeader): bool {
$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $secret);
return hash_equals($expected, $signatureHeader);
}
Des erreurs qui compromettent la sécurité
1. Utilisation de == au lieu d'une comparaison en temps constant
Comparaison standard de chaînes (==, ===) court-circuite dès qu'elle trouve une incohérence. Cela crée un canal de temps: un attaquant peut mesurer combien de temps votre serveur met pour rejeter différentes signatures. Les chaînes partageant un préfixe plus long prennent un peu plus de temps à être rejetées. Avec suffisamment de requêtes, un attaquant peut utiliser cela pour reconstruire une signature valide, byte par byte.
Utilisez toujours une comparaison en temps constant : hmac.compare_digest() en Python, crypto.timingSafeEqual() dans Node.js, hash_equals() dans PHP.
2. Analyse du corps avant la vérification
L'HMAC est calculé sur les octets bruts du corps de la requête. Si vous analysez d'abord le JSON puis que vous le re-sérialisez pour vérifier, vous pourriez obtenir une séquence de caractères différente (ordre de clés différent, espaces, encodage). Capturé toujours le corps brut avant que le parser de votre framework ne l'ait touché, puis vérifiez à partir de ce corps.
3. Pas de vérification des attaques de répétition
Une requête signée est valide pour toujours — sauf si vous vérifiez la date. Si une plateforme inclut une date dans son schéma de signature (Stripe le fait ; GitHub ne le fait pas), rejetez les requêtes où la date est antérieure à quelques minutes. Cela empêche un attaquant de capturer et de répéter une requête légitime.
4. Inclusion de la clé dans le code source
Les clés de webhook doivent être stockées dans des variables d'environnement ou un gestionnaire de secrets, jamais commitées dans le contrôle de version. Une clé perdue signifie qu'un attaquant peut fabriquer n'importe quel payload pour toujours — jusqu'à ce que vous le rotiez.
Ce que l'HMAC ne protège pas
L'HMAC prouve que le payload a été signé par quelqu'un possédant la clé. Il ne pas protège pas contre :
- Un expéditeur compromis — si l'infrastructure de signature de Stripe était compromise, des événements faux auraient encore des signatures valides
- Attaques de répétition — sauf si vous validez également une date ou un nonce
- La confidentialité — l'HMAC ne chiffre rien ; le payload voyage en clair (bien que HTTPS le gère)
Pour la plupart des intégrations de webhooks, l'HMAC sur HTTPS couvre tout ce dont vous avez besoin.
Installez nos extensions
Ajoutez des outils IO à votre navigateur préféré pour un accès instantané et une recherche plus rapide
恵 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 !
Outils essentiels
Tout voir Nouveautés
Tout voirMise à jour: Notre dernier outil a été ajouté le 12 juin 2026
