En une phrase
HMAC est une poignée de main cryptographique qui utilise une clé secrète partagée pour prouver qu'un message est authentique et n'a pas été bidouillé.
Le problème que ça résout
Au début de l'internet, à l'époque du Far West numérique, envoyer un message revenait à envoyer une carte postale. Quiconque l'interceptait pouvait le lire, et même gribouiller dessus avant de le faire suivre. Si vous receviez une carte postale disant « Rendez-vous à minuit, apportez l'argent », comment pouviez-vous être sûr qu'elle venait vraiment de votre contact agent secret, et non de sa Némésis, la maléfique Ève ? Et comment être sûr que le message original n'était pas « Rendez-vous à midi pour un déjeuner amical » ?
C'est le double problème de l'authenticité (est-ce que ça vient vraiment de vous ?) et de l'intégrité (est-ce que ça a été modifié ?).
Une simple fonction de hachage cryptographique (comme SHA-256) semble être une bonne première étape. Vous pourriez hasher votre message, envoyer le message et le hash, et le destinataire pourrait re-hasher le message pour voir s'ils correspondent. Génial ! Ça résout l'intégrité. Si un seul octet du message était changé, les hashs ne correspondraient pas.
Mais ça ne résout pas l'authenticité. La maléfique Ève peut simplement changer le message, calculer un nouveau hash pour son nouveau message, et envoyer les deux. Le destinataire verra que le hash correspond au message, mais il n'aura aucun moyen de savoir que le tout est un faux.
C'est là que le HMAC (Hash-based Message Authentication Code) entre en scène. En introduisant une clé secrète partagée dans le processus de hachage, le HMAC crée une signature que seule une personne possédant cette clé secrète peut produire. C'est la différence entre un simple sceau de cire (n'importe qui peut en faire un) et un sceau de cire fait avec une chevalière unique (seul le roi en possède une). Le HMAC nous apporte à la fois l'intégrité et l'authenticité.
Comment ça marche sous le capot
Le HMAC n'est pas un nouveau type de fonction de hachage ; c'est une recette qui utilise des fonctions de hachage existantes (comme SHA-256) d'une manière astucieuse et spécifique. La spécification officielle est la RFC 2104, mais décomposons ça en langage simple.
Les Ingrédients
Pour concocter un HMAC, il vous faut trois choses :
- Le Message : Les données que vous voulez protéger. Ça pourrait être un payload JSON pour un webhook, une chaîne de paramètres d'URL, ou n'importe quel bloc de données binaires.
- La Clé Secrète : Une chaîne d'octets connue uniquement de l'expéditeur et du destinataire. C'est l'ingrédient magique. Si elle est compromise, tout le système est fichu.
- La Fonction de Hachage : Un algorithme standard comme SHA-1, SHA-256, ou SHA-512. Le choix de la fonction de hachage détermine la longueur de la signature HMAC finale (par ex., HMAC-SHA256 produit une signature de 256 bits).
La Recette (L'Algorithme HMAC)
On pourrait penser qu'il suffit de faire hash(clé + message). Ça semble simple, mais cette construction est vulnérable à des magouilles cryptographiques astucieuses appelées « attaques par extension de longueur » (length extension attacks). La construction officielle du HMAC est un peu plus complexe, spécifiquement pour empêcher ces attaques. Elle utilise un processus de double hachage.
Voici un aperçu simplifié des étapes :
Préparer la Clé : La fonction de hachage opère sur des blocs de données de taille fixe (par ex., 64 octets pour SHA-256). La clé doit être préparée pour s'adapter à cette taille de bloc.
- Si la clé est plus longue que la taille du bloc, on hashe la clé elle-même et on utilise ce résultat comme nouvelle clé.
- Si la clé est plus courte que la taille du bloc, on la complète avec des octets nuls jusqu'à ce qu'elle atteigne la taille du bloc.
Créer les Clés Interne et Externe : À partir de cette clé préparée, on dérive deux clés distinctes.
ipad(inner pad) : un octet constant (0x36) répété pour remplir la taille du bloc.opad(outer pad) : un octet constant différent (0x5C) répété pour remplir la taille du bloc.
On crée une
inner_padded_keyen prenant notre clé préparée et en lui appliquant un XOR avecipad. On crée uneouter_padded_keyen appliquant un XOR à la clé préparée avecopad.Effectuer le Double Hash : Maintenant, le plat de résistance.
- Hash Interne : Concaténer la
inner_padded_keyavec le message original, et passer le tout dans la fonction de hachage. - Hash Externe : Concaténer la
outer_padded_keyavec le résultat du hash interne, et passer ce résultat dans la fonction de hachage.
- Hash Interne : Concaténer la
Le résultat final de ce hash externe est votre signature HMAC !
En pseudo-code, ça ressemble à ça :
function hmac(key, message, hash_function, block_size) {
// 1. Prepare the key
if (key.length > block_size) {
key = hash_function(key);
}
if (key.length < block_size) {
key = pad_with_zeros(key, block_size);
}
// 2. Create inner and outer padded keys
o_key_pad = key XOR (0x5C repeated to block_size);
i_key_pad = key XOR (0x36 repeated to block_size);
// 3. Perform the double hash
inner_hash_result = hash_function(i_key_pad + message);
final_hmac = hash_function(o_key_pad + inner_hash_result);
return final_hmac;
}
Pourquoi le Double Hash ?
Cette structure interne-puis-externe, c'est l'astuce secrète. Le hash interne combine le secret et le message. Le hash externe, pour l'essentiel, hashe à nouveau le résultat de la première opération avec le secret. Cela « scelle » le hash interne. Ça rend informatiquement impossible pour un attaquant de manipuler le résultat du hash intermédiaire sans connaître la clé, déjouant ainsi les attaques par extension de longueur et autres failles cryptographiques potentielles. C'est une construction éprouvée et robuste qui a résisté à l'épreuve du temps.
Histoires vécues
Le Gardien des Webhooks GitHub
L'équipe d'une startup avait configuré son serveur d'intégration continue (CI) pour déployer automatiquement leur application en production chaque fois que quelqu'un poussait sur la branche main. Le déclencheur était un webhook de GitHub : une requête POST envoyée depuis les serveurs de GitHub vers une URL publique sur leur serveur de CI. Une nuit, leur stagiaire blagueur a trouvé l'URL publique et, avec une simple commande cURL, a commencé à envoyer de faux payloads de webhook, déclenchant des dizaines de déploiements inutiles et gourmands en ressources.
La développeuse senior a réglé ça en 15 minutes. Dans les paramètres du webhook de GitHub, elle a généré un « secret » long et aléatoire. Elle a copié ce secret et l'a configuré comme variable d'environnement sur leur serveur de CI. GitHub utilisait désormais ce secret pour générer une signature HMAC-SHA256 pour chaque payload de webhook, l'envoyant dans un header X-Hub-Signature-256. Le code du serveur de CI a été mis à jour pour effectuer le même calcul HMAC sur le corps brut de la requête (raw request body) qu'il recevait, en utilisant le même secret. Si sa signature calculée correspondait à celle du header, la requête était traitée. Sinon, elle était rejetée avec un 403 Forbidden. Les blagues ont cessé immédiatement.
Leçon : Sécurisez toujours vos webhooks avec une vérification de signature HMAC. Ne faites confiance à aucune requête entrante tant qu'elle n'a pas été authentifiée.
Sécuriser la boîte à cookies de l'API
Un développeur créait un service qui utilisait un simple cookie signé pour l'authentification. Quand un utilisateur se connectait, le serveur émettait un cookie contenant son user_id et un timestamp d'expiration. Pour empêcher les utilisateurs de modifier leur cookie pour devenir un autre utilisateur (par ex., changer user_id=123 en user_id=1), le développeur a inclus une signature HMAC.
Le payload du cookie ressemblait à quelque chose comme : user_id=123&expiry=1678886400. Le serveur signait cette chaîne exacte avec une clé secrète stockée sur le serveur. Le cookie final envoyé au navigateur était data="user_id=123&expiry=1678886400"&signature="sha1=2a8b...".
Lorsque l'utilisateur faisait une requête ultérieure, son navigateur renvoyait le cookie. Le serveur prenait la partie data, recalculait la signature HMAC avec sa clé secrète, et la comparait à la partie signature du cookie. Si elles correspondaient, le serveur savait que l'ID utilisateur et la date d'expiration étaient légitimes et n'avaient pas été trafiqués.
Leçon : HMAC est un moyen fantastique de créer des tokens ou des cookies « stateless » et infalsifiables, formant la base de nombreux systèmes d'authentification, y compris les JSON Web Tokens (JWT).
Le virement bancaire qui n'a pas été détourné
Une plateforme e-commerce s'est intégrée à l'API d'un processeur de paiement pour initier des virements à ses vendeurs. L'appel API était un simple message JSON : {"vendor_id": "ven_abc", "amount": 500.00, "currency": "USD"}. La plateforme s'inquiétait d'une attaque de l'homme du milieu (Man-in-the-Middle, MITM). Même en HTTPS, qui chiffre le trafic, un attaquant sophistiqué pourrait (dans certains scénarios théoriques, comme avec une autorité de certification compromise) intercepter et modifier la requête. Il pourrait changer le amount à 50000.00 ou le vendor_id pour le sien.
L'API du processeur de paiement exigeait que chaque requête soit signée avec HMAC-SHA512. La plateforme sérialisait le payload JSON en une chaîne canonique, calculait la signature avec sa clé d'API privée, et l'envoyait dans un header Authorization. Les serveurs du processeur de paiement effectuaient exactement les mêmes étapes. Si leur signature calculée correspondait à celle envoyée dans le header, ils savaient deux choses avec certitude : la requête provenait de la plateforme légitime (authenticité) et le vendor_id et le amount n'avaient pas été altérés en transit (intégrité).
Leçon : Pour les opérations à fort enjeu, le HMAC fournit une couche de sécurité critique pour garantir que l'expéditeur et le contenu d'un message sont bien ceux que vous attendez.
Erreurs et pièges courants
- Faire fuiter la clé secrète. La clé, c'est tout. Si vous l'exposez dans du JavaScript côté client, la commitez dans un dépôt Git public, ou la loguez en texte clair, votre sécurité est fichue. Traitez-la comme un mot de passe.
- Utiliser une comparaison qui n'est pas en temps constant. Quand vous vérifiez si la signature fournie par l'utilisateur correspond à celle que vous avez calculée, une comparaison de chaînes standard comme
if (a === b)peut être une faille de sécurité. Elle renvoie souventfalsedès qu'elle trouve un caractère différent. Cela crée une minuscule différence de temps que les attaquants peuvent mesurer pour deviner la signature caractère par caractère. C'est une « attaque temporelle » (timing attack). Utilisez toujours une fonction de comparaison dédiée, « en temps constant », provenant d'une bibliothèque de cryptographie, qui prend le même temps quel que soit l'endroit où se trouve la différence. - Signer les mauvaises données. L'expéditeur et le destinataire doivent calculer le HMAC sur la séquence d'octets exacte. Un bug courant est qu'une partie signe un objet JSON joliment formaté (prettified) tandis que l'autre signe la version compacte sur une seule ligne. Ou qu'une partie inclut un retour à la ligne final et pas l'autre. Vous devez vous mettre d'accord sur un format de message canonique et vous y tenir.
- Oublier les attaques par rejeu (replay attacks). Le HMAC en lui-même n'empêche pas un attaquant de capturer un message valide et signé et de le renvoyer encore et encore. Si ce message est « payer 10 $ à Bob », vous ne voulez pas que l'attaquant puisse déclencher ce paiement 1000 fois. Pour éviter cela, incluez une valeur qui change à chaque requête — comme un timestamp ou un « nonce » (nombre utilisé une seule fois) — à l'intérieur des données qui sont signées. Le serveur peut alors vérifier le timestamp pour rejeter les vieilles requêtes ou conserver une liste des nonces déjà utilisés pour rejeter les doublons.
Pourquoi vous devriez y prêter attention
Vous devriez penser au HMAC chaque fois que vous gérez une communication qui doit être digne de confiance. Il ne s'agit pas de garder les données secrètes (ça, c'est le travail du chiffrement), mais de s'assurer que les données sont légitimes.
- Vous construisez ou consommez des APIs ? Surtout avec les webhooks (de services comme Stripe, GitHub, Twilio), le HMAC est le standard de l'industrie pour vérifier que la requête est authentique.
- Vous travaillez sur l'authentification ? De nombreux systèmes à base de tokens, dont les plus célèbres sont les JWT, utilisent le HMAC (par ex., l'algorithme 'HS256') pour signer le payload du token, empêchant les utilisateurs de modifier leurs propres permissions.
- Besoin de vérifier l'intégrité des données ? Si vous faites transiter des données dans un environnement non fiable (comme le navigateur d'un utilisateur via un cookie) et que vous devez vous assurer qu'elles reviennent intactes, le HMAC est votre outil.
C'est une primitive fondamentale dans la boîte à outils de sécurité d'un développeur web. Comprendre son fonctionnement fera de vous un meilleur ingénieur, plus soucieux de la sécurité.
Pour aller plus loin
- RFC 2104: HMAC: Keyed-Hashing for Message Authentication: La spécification technique originale. C'est dense, mais c'est la source de vérité ultime.
- Wikipedia: HMAC: Un excellent aperçu de haut niveau de l'historique, des principes de conception et des détails d'implémentation.
- OWASP: Replay Attack: La page de l'Open Web Application Security Project sur les attaques par rejeu, un concept essentiel à comprendre lors de l'utilisation du HMAC.
- Stripe Docs: Checking webhook signatures: Un guide pratique et concret d'une entreprise qui s'appuie massivement sur le HMAC pour sécuriser des milliards de dollars de transactions.
- Crypto 101: HMAC: Une explication un peu plus accessible des principes cryptographiques derrière la conception du HMAC.