Cryptographie appliquée

Différence hash, HMAC, signature - Guide de décision crypto

Hash vs HMAC vs signature décryptés : propriétés, cas d'usage, algorithmes, arbre de décision, pièges fréquents. Pour choisir sans se tromper.

Naim Aouaichia
17 min de lecture
  • Cryptographie
  • Hash
  • HMAC
  • Signatures
  • SHA-256
  • Ed25519
  • Fondamentaux

Hash, MAC/HMAC et signature numérique sont trois primitives cryptographiques régulièrement confondues par les développeurs, et même par certains architectes sécurité expérimentés. Les trois produisent "une petite valeur qui prouve quelque chose sur un message", mais les garanties sont très différentes : un hash ne prouve que l'intégrité face à des erreurs aléatoires, un HMAC ajoute l'authenticité entre parties qui partagent un secret, une signature ajoute la non-répudiation vers un tiers. Ce guide clarifie, avec arbre de décision, cas d'usage réels et pièges fréquents.

1. Vue d'ensemble - matrice des propriétés

1.1 Les 3 primitives, les 4 propriétés

PrimitiveIntégritéAuthenticitéNon-répudiationClé
Hash (SHA-256)Oui (contre erreurs)NonNonAucune
HMAC (HMAC-SHA-256)Oui (même contre attaquant)Oui (au détenteur du secret)NonSecret partagé
Signature (Ed25519)OuiOuiOuiClé privée, vérification avec clé publique

1.2 Question de base à se poser

"Pour me convaincre que ce message n'a pas été modifié et vient bien d'Alice, de quoi est-ce que j'ai besoin ?"

  • Juste détecter une corruption réseau : hash suffit.
  • Prouver que le message vient d'Alice, et Alice et moi partageons un secret : HMAC.
  • Prouver à un tiers qu'Alice a bien émis ce message, sans partager de secret avec lui : signature.

1.3 Conséquence directe

Ces trois primitives ne sont pas interchangeables. Utiliser un hash là où un HMAC est requis = vulnérabilité. Utiliser un HMAC là où une signature est requise = impossibilité de prouver l'origine à un tiers.

2. Hash functions - intégrité sans secret

2.1 Définition

Une fonction de hachage H prend un message de taille arbitraire et produit une empreinte (digest) de taille fixe.

H("Hello, World!")  = dffd6021bb2bd5b0af676290809ec3a5...
H("Hello, World?")  = 8c9d5dd7e53f3a3e9c7a78a2aeeae3d1...

Un bit de différence en entrée → moitié des bits différents en sortie (effet avalanche).

2.2 Propriétés attendues d'une hash cryptographique

Trois propriétés formelles :

  • Preimage resistance : étant donné h, impossible de trouver m tel que H(m) = h.
  • Second preimage resistance : étant donné m et H(m), impossible de trouver m' ≠ m tel que H(m') = H(m).
  • Collision resistance : impossible de trouver deux messages m1, m2 tels que H(m1) = H(m2).

"Impossible" signifie ici computationnellement infaisable avec les ressources actuelles et futures raisonnables.

2.3 Les familles principales en 2026

FamilleSortie standardStatut
MD5128 bitsCassée (collisions en secondes). Usage crypto interdit.
SHA-1160 bitsCassée (SHAttered 2017). Déconseillée fortement.
SHA-2 (SHA-256, SHA-512)256 / 512 bitsSûre, standard le plus adopté.
SHA-3 (Keccak)256 / 512 bitsSûre. Design très différent de SHA-2.
BLAKE2 (BLAKE2b, BLAKE2s)256 / 512 bitsSûre, plus rapide que SHA-2. Utilisée par WireGuard, Argon2.
BLAKE3VariableModerne, parallélisable, très rapide.

Par défaut 2026 :

  • SHA-256 si interop / standard.
  • SHA-3 / BLAKE2 / BLAKE3 si performance ou indépendance structurelle importante.

2.4 Ce qu'un hash N'EST PAS

Un hash SEUL ne garantit pas l'authenticité. Exemple :

  • Alice publie un fichier archive.zip avec son hash SHA-256.
  • L'attaquant intercepte, remplace archive.zip par sa version malveillante, et recalcule le hash SHA-256 (la fonction est publique, pas de secret).
  • Bob télécharge, compare : le hash matche. Il est piégé.

Hash prouve qu'un message n'a pas été corrompu accidentellement (bit flip réseau, erreur disque), pas qu'il n'a pas été intentionnellement modifié par un attaquant qui contrôle le canal.

2.5 Usages légitimes de hash sans secret

  • Fingerprint de fichier pour dedup : Git utilise SHA-1 (historique) / SHA-256 pour identifier les objets.
  • Content-addressable storage : IPFS, Sigstore Rekor, Docker image layers.
  • Checksum pour intégrité non adversariale : vérifier qu'un download n'est pas corrompu.
  • Dedup et index : bases de données, cache keys.
  • Merkle trees : Git, blockchain, certificate transparency.

2.6 Password hashing - cas particulier

Ne jamais utiliser SHA-256 directement pour stocker des mots de passe. Un attaquant qui vole la base peut tester des milliards de candidats par seconde sur GPU.

Pour mots de passe : KDFs lents, paramétrables :

  • Argon2id (recommandé OWASP 2024).
  • bcrypt.
  • scrypt.
  • PBKDF2 (600 000 iterations min pour SHA-256).

Ces fonctions sont volontairement coûteuses (10-100 ms par calcul) pour rendre le bruteforce non viable.

3. HMAC - intégrité + authenticité avec secret partagé

3.1 Définition

Un MAC (Message Authentication Code) prend un message m et une clé secrète K, et produit un tag :

tag = MAC(K, m)

Le récepteur, qui possède aussi K, recalcule MAC(K, m) et compare. Si égal, le message est authentique (provient d'un détenteur de K) et intègre (n'a pas été modifié).

3.2 HMAC - construction standard

HMAC (Hash-based MAC) construit un MAC à partir d'une hash function de façon prouvablement sûre (Bellare, Canetti, Krawczyk 1996, RFC 2104).

Formule simplifiée :

HMAC(K, m) = H((K XOR opad) || H((K XOR ipad) || m))

ipad et opad sont des constantes, H est SHA-256 ou équivalent.

Choix standard 2026 : HMAC-SHA-256 (HS256) ou HMAC-SHA-512.

3.3 Propriétés HMAC

  • Intégrité : modification du message = tag incompatible.
  • Authenticité : seul un détenteur de K peut produire un tag valide.
  • PAS de non-répudiation : Alice et Bob partagent K, donc Alice peut prétendre que c'est Bob qui a émis le message, et réciproquement. Un tiers ne peut pas départager.

3.4 Usages légitimes de HMAC

  • Signature de webhook : Stripe, GitHub, Slack signent leurs webhooks sortants avec HMAC-SHA-256 et un secret partagé avec le consommateur. Exemple Stripe : header Stripe-Signature: t=timestamp,v1=HMAC-SHA-256(secret, timestamp+payload).
  • Cookies de session signés : Django, Flask-Login signent les cookies côté serveur avec HMAC pour prévenir tampering.
  • JWT HS256 : variante JWT avec HMAC-SHA-256 pour signature (voir JWT risques).
  • API requests signing : AWS SigV4 utilise HMAC-SHA-256 pour signer les requêtes API.
  • Validation d'intégrité inter-services dans environnement partageant un secret.

3.5 HMAC vs autres MACs

  • CMAC : MAC basé sur un block cipher (AES-CMAC). OK mais moins courant.
  • Poly1305 : MAC extrêmement rapide, utilisé dans ChaCha20-Poly1305 et Wireguard. Spécifique (nonce unique requis).
  • KMAC : MAC basé sur SHA-3 / Keccak.

Pour usage général, HMAC-SHA-256 est le choix par défaut, universellement supporté.

3.6 Le piège de la comparaison

Comparer un tag reçu avec le tag attendu doit se faire en constant-time. Une comparaison naïve (memcmp, == en JS) s'arrête au premier byte différent, créant une fuite temporelle qui permet de bruteforcer byte par byte.

Mauvais :

if received_tag == computed_tag:  # PIEGE
    ...

Bon :

import hmac
if hmac.compare_digest(received_tag, computed_tag):  # constant-time
    ...

Toutes les bibliothèques crypto fournissent une fonction constant-time. Toujours l'utiliser.

4. Signatures numériques - intégrité + authenticité + non-répudiation

4.1 Définition

Dans une signature numérique, Alice possède :

  • Une clé privée sk gardée secrète.
  • Une clé publique pk partageable librement.

Alice signe un message m :

signature = Sign(sk, m)

Quiconque possède pk peut vérifier :

Verify(pk, m, signature) -> true / false

4.2 Propriétés

  • Intégrité : modification du message = signature invalide.
  • Authenticité : seul le détenteur de sk peut produire une signature valide.
  • Non-répudiation : comme sk est secret à Alice, elle ne peut nier avoir signé. Un tiers, avec pk, peut prouver que c'est bien Alice.

4.3 Algorithmes standard 2026

AlgorithmeBaseUsage
Ed25519 (EdDSA)Courbes EdwardsRecommandation 2026. Rapide, robuste, signature 64 octets.
ECDSA P-256Courbes NISTStandard FIPS. Prudence sur implémentation (nonce k unique).
ECDSA P-384 / P-521Courbes NISTHauts niveaux de sécurité.
RSA-PSSRSAInterop / legacy. Préférer à RSA PKCS#1 v1.5.
RSA PKCS#1 v1.5RSAHistorique (JWT RS256). Nombreuses attaques, à éviter en nouveaux projets.
ML-DSA (Dilithium)Lattice PQPost-quantum, NIST FIPS 204 (2024).

Par défaut 2026 : Ed25519 pour nouveaux projets. ES256 (ECDSA P-256) ou RS256 (RSA) selon exigences d'interop.

4.4 Usages légitimes de signatures

  • Certificats X.509 TLS : CA signe le certificat du domaine. Autorité → domaine → client navigateur qui vérifie.
  • Code signing : Microsoft Authenticode (binaires Windows), Apple codesign (apps macOS/iOS), Cosign (images Docker, artefacts).
  • Git commits signés : GPG ou SSH signing attribue un commit à une identité cryptographique.
  • SSH keys : authentification serveur et client.
  • JWT RS256, ES256, EdDSA : tokens émis par un IdP, vérifiés par des services downstream via clé publique.
  • Blockchain : chaque transaction signée par la clé privée du wallet.
  • Documents électroniques eIDAS : signatures qualifiées avec valeur juridique équivalente à la signature manuscrite.

4.5 Cas où signature est NÉCESSAIRE (HMAC ne suffit pas)

  • Vous voulez qu'un tiers (auditeur, client, juge) puisse vérifier l'authenticité sans recevoir votre secret.
  • Vous voulez pouvoir prouver juridiquement qu'une personne donnée a émis un document.
  • Vous distribuez des artefacts publiquement (images Docker, binaires, packages) et quiconque doit pouvoir vérifier.

Dans ces cas, HMAC est structurellement inadapté.

4.6 Cas où HMAC suffit (signature overkill)

  • Communication entre services qui partagent déjà un secret (inter-service dans même organisation).
  • Webhook entre deux partenaires qui ont échangé un secret.
  • Cookie de session signé qui n'est vérifié que par le serveur émetteur.

Dans ces cas, HMAC est plus simple, plus rapide, et suffisant.

5. Arbre de décision

Face à un besoin, suivre cet arbre :

Besoin de détecter intégrité ?
├─ Non (besoin autre, ex. dedup) → fonction non-crypto
└─ Oui → lire la suite

Contre qui ?
├─ Erreurs aléatoires (réseau, disque) → hash suffit (SHA-256, BLAKE2)
└─ Attaquant → lire la suite

Qui va vérifier ?
├─ Seul l'émetteur (ou un groupe partageant un secret)
│   └─ HMAC-SHA-256 (ou HMAC-SHA-512)
└─ Un tiers qui n'a pas le secret
    ├─ Signature → lire la suite
    │
    └─ Choix de l'algorithme ?
        ├─ Nouveau projet → Ed25519
        ├─ Interop NIST required → ECDSA P-256 (ES256)
        ├─ Legacy JWT / interop large → RSA (RS256 ou PS256)
        └─ Post-quantum requirement → ML-DSA (Dilithium, FIPS 204)

6. Tableau comparatif synthétique

AspectHashHMACSignature
Clé requiseAucuneSecret partagéPaire privée/publique
Distribution cléN/AOut-of-band avant usageClé publique partageable librement
Vitesse (typical)Ultra rapide (GB/s)Rapide (GB/s)Plus lent (ms par signature/vérif)
Taille sortie256-512 bits256-512 bits256-512 octets selon algo
IntégritéOui (contre erreurs)Oui (même vs attaquant)Oui
AuthenticitéNonOui (au groupe qui partage K)Oui (au détenteur de sk)
Non-répudiationNonNonOui
Vérifiable par tiersN/ASi tiers a le secretOui sans secret
Standard recommandé 2026SHA-256, BLAKE2HMAC-SHA-256Ed25519
Bibliothèque typehashlib.sha256hmac.newPyNaCl / cryptography

7. Erreurs classiques

7.1 Utiliser un hash pour authentifier

Classique : un service distribue des webhooks avec X-Signature: SHA-256(payload) et pense que c'est signé. Faux. N'importe qui peut recalculer. Il faut HMAC-SHA-256(secret, payload).

7.2 Comparer les tags en non constant-time

Vu §3.6. Le plus fréquent dans le code custom.

7.3 Utiliser SHA pour stocker des passwords

Sur GPU moderne, des milliards de SHA par seconde. Base fuitée = passwords cassés en heures. Utiliser Argon2id.

7.4 HMAC avec une clé trop courte

HMAC-SHA-256 avec une clé de 8 bytes = bruteforce possible. Minimum 32 bytes aléatoires (256 bits d'entropie).

7.5 Clé HMAC connue de trop de parties

Si 50 équipes différentes partagent le même secret HMAC pour signer leurs messages inter-services, il devient impossible d'attribuer une signature à une équipe spécifique → plus vraiment d'authenticité utile. Mitigation : une paire de secrets par paire de services, ou passer aux signatures.

7.6 RSA PKCS#1 v1.5 signature parsing

Bug Bleichenbacher et variantes récurrentes. Ne pas utiliser PKCS#1 v1.5 pour nouveaux systèmes. Utiliser PSS.

7.7 Signature sans timestamp ni expiration

Une signature sans contexte temporel est rejouable à vie. Inclure un timestamp ou nonce dans le message signé (comme Stripe avec t=timestamp dans la signature du webhook).

7.8 Confondre hash de fichier et signature d'artefact

Publier un fichier avec son hash SHA-256 sur le même site ne signe rien : attaquant qui compromet le site change les deux. Il faut soit un canal séparé (autre domaine, DNSSEC, Certificate Transparency), soit une signature vérifiable avec une clé publique établie préalablement.

8. Cas avancés - keyed hash et constructions modernes

8.1 Keyed BLAKE2

BLAKE2 supporte nativement une clé optionnelle, ce qui en fait à la fois une hash et un MAC :

import hashlib
tag = hashlib.blake2b(message, key=secret_key, digest_size=32).digest()

Plus rapide que HMAC-SHA-256, pas de construction "HMAC around".

8.2 KMAC (Keccak MAC)

MAC basé sur SHA-3. Alternative moderne à HMAC, conçue spécifiquement pour Keccak.

8.3 Signatures avancées

Cas de niche mais utiles dans certains contextes :

  • Ring signatures : prouve qu'un signataire est dans un groupe sans révéler lequel.
  • Blind signatures : signer un message sans voir son contenu (utilisé par certains cash systems).
  • Threshold signatures : une signature valide nécessite un quorum de signataires.
  • Aggregate signatures : combiner N signatures en une seule plus compacte.

Usages : crypto-monnaies, e-voting, anonymat. Hors scope pour la plupart des applications.

8.4 Post-quantum signatures

Le NIST a publié en 2024 :

  • ML-DSA (Dilithium, FIPS 204) : standard principal.
  • SLH-DSA (SPHINCS+, FIPS 205) : alternative hash-based.

Signatures PQ plus grandes que ECDSA/Ed25519 (1-4 KB vs 64 bytes), plus lentes. Déploiement en hybride (Ed25519 + Dilithium) pour la transition.

9. Implémentation pratique par cas d'usage

9.1 Vérifier un webhook Stripe

import hmac
import hashlib
import time
 
def verify_stripe_webhook(payload: bytes, sig_header: str, secret: bytes) -> bool:
    # Parser Stripe-Signature: t=1720000000,v1=abc123...
    parts = dict(item.split("=") for item in sig_header.split(","))
    timestamp = parts["t"]
    signature = parts["v1"]
    
    # Rejeter si timestamp trop ancien (anti-replay)
    if abs(time.time() - int(timestamp)) > 300:
        return False
    
    # Recalculer HMAC
    signed_payload = f"{timestamp}.".encode() + payload
    expected = hmac.new(secret, signed_payload, hashlib.sha256).hexdigest()
    
    # Comparaison constant-time
    return hmac.compare_digest(expected, signature)

9.2 Signer un artefact avec Cosign (keyless)

# Signer une image Docker avec OIDC keyless
cosign sign --yes registry.example.com/app@sha256:abc123
 
# Vérifier la signature
cosign verify \
  --certificate-identity="https://github.com/example/app/.github/workflows/release.yml@refs/heads/main" \
  --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
  registry.example.com/app@sha256:abc123

Cosign utilise Ed25519 ou ECDSA. Signature enregistrée dans Rekor (transparency log public).

9.3 Fingerprint de contenu (hash)

import hashlib
 
def content_fingerprint(data: bytes) -> str:
    return hashlib.sha256(data).hexdigest()

Usage : dedup, cache keys, content-addressable storage. Pas besoin de secret.

import hmac
import hashlib
import base64
import json
 
def sign_cookie(payload: dict, secret: bytes) -> str:
    payload_bytes = json.dumps(payload, sort_keys=True).encode()
    tag = hmac.new(secret, payload_bytes, hashlib.sha256).hexdigest()
    return base64.b64encode(payload_bytes).decode() + "." + tag
 
def verify_cookie(cookie: str, secret: bytes) -> dict | None:
    try:
        payload_b64, tag = cookie.rsplit(".", 1)
        payload_bytes = base64.b64decode(payload_b64)
        expected_tag = hmac.new(secret, payload_bytes, hashlib.sha256).hexdigest()
        if not hmac.compare_digest(expected_tag, tag):
            return None
        return json.loads(payload_bytes)
    except Exception:
        return None

Pattern utilisé par Django, Flask-Login, Express signed-cookies.

9.5 Vérifier un commit Git signé

# Afficher les signatures des commits
git log --show-signature
 
# Vérifier un commit spécifique
git verify-commit HEAD
 
# Exiger les commits signés sur une branche (config)
git config --global commit.gpgsign true

GitHub, GitLab affichent un badge "Verified" pour les commits signés par une clé GPG/SSH liée à un compte.

10. Checklist - choisir la bonne primitive

Pour détecter une corruption aléatoire (réseau, disque)

  • Hash (SHA-256, BLAKE2, BLAKE3)

Pour identifier du contenu (dedup, cache, Git, IPFS)

  • Hash cryptographique (SHA-256, BLAKE3)

Pour vérifier l'intégrité face à un adversaire, avec secret partagé

  • HMAC-SHA-256 ou équivalent
  • Clé minimum 256 bits
  • Comparaison constant-time
  • Rotation périodique de la clé

Pour signer un webhook, un token interne, un cookie

  • HMAC-SHA-256 ou équivalent
  • Inclure timestamp pour anti-replay
  • Secret par service, pas global

Pour stocker des mots de passe

  • Argon2id (recommandé)
  • bcrypt (acceptable)
  • PBKDF2-SHA-256 avec 600000+ iterations
  • Jamais SHA-* seul, jamais MD5/SHA-1

Pour signer un artefact distribué publiquement

  • Ed25519 ou ECDSA P-256
  • Vérification accessible publiquement via clé publique
  • Cosign keyless pour containers/artefacts

Pour signer un document juridiquement opposable (eIDAS)

  • Certificat qualifié + signature Ed25519 / ECDSA / RSA-PSS via prestataire certifié
  • Horodatage qualifié

Pour JWT

  • RS256 / ES256 / EdDSA pour interop multi-consumers (signature)
  • HS256 pour service mono-consommateur (HMAC)
  • Jamais alg: none, algorithme fixé côté serveur

Pour code signing enterprise

  • Microsoft Authenticode (Windows binaries)
  • Apple codesign (macOS/iOS apps)
  • Cosign (containers, artefacts)

11. Post-quantum - impact différentiel

Grover divise la sécurité symétrique et hash par 2 :

  • SHA-256 → 128 bits effectifs contre collision (acceptable).
  • SHA-512 → 256 bits (confortable).
  • HMAC-SHA-256 avec clé 256 bits → 128 bits effectifs (acceptable).

Shor casse la cryptographie asymétrique classique :

  • Ed25519, ECDSA, RSA → cassés.
  • ML-DSA (Dilithium) → nouveau standard PQ.
  • SLH-DSA (SPHINCS+) → alternative hash-based.

Impact : les hashes et HMAC ont juste besoin de tailles augmentées, les signatures nécessitent un changement complet d'algorithme. D'où l'urgence relative : le symétrique attend, l'asymétrique migre dès maintenant pour le long terme.

12. Verdict et posture Zeroday

La confusion hash / HMAC / signature est la confusion crypto la plus fréquente dans le code en production en 2026. Elle cause des vulnérabilités évitables : webhooks "signés" par hash recalculable, passwords stockés en SHA-256, signatures HMAC quand une vraie signature asymétrique était nécessaire.

Pour un développeur : retenir les trois propriétés (intégrité, authenticité, non-répudiation) et les associer aux trois primitives. Cette grille mentale élimine 80 % des confusions. Pas besoin d'être cryptographe, juste de savoir poser la bonne question avant de coder.

Pour un AppSec : le check systématique à faire dans tout audit crypto - "est-ce que le bon outil est utilisé pour le bon besoin ?" - révèle régulièrement des patterns fautifs même dans du code récent.

Pour une organisation : documenter un guide interne de choix crypto (arbre de décision §5) consultable par tous les développeurs produit des gains durables. Coût faible, impact élevé.

Pour approfondir : qu'est-ce que la cryptographie pour le panorama, chiffrement symétrique expliqué pour les AEAD qui combinent chiffrement et authentification, JWT : risques et bonnes pratiques pour l'application directe HS256/RS256/ES256/EdDSA dans les tokens, gestion de session sécurisée pour les cookies signés HMAC en pratique web.

Écrit par

Naim Aouaichia

Expert cybersécurité et fondateur de Zeroday Cyber Academy

Expert cybersécurité avec un master spécialisé et un parcours hybride : développement, DevOps, DevSecOps, SOC, GRC. Fondateur de Hash24Security et Zeroday Cyber Academy. Formateur et créateur de contenu technique sur la cybersécurité appliquée, la sécurité des LLM et le DevSecOps.