HMAC en Python — Guide hmac.new() SHA-256 + exemples de code

·DevOps Engineer & Python Automation Specialist·Révisé parMaria Santos·Publié

Utilisez le Générateur HMAC gratuit directement dans votre navigateur — sans installation.

Essayer Générateur HMAC en ligne →

Chaque callback webhook, chaque requête API signée, chaque notification d'événement Stripe ou GitHub utilise une signature HMACpour prouver que le payload n'a pas été altéré. Le module hmac de Python gère le HMAC-SHA256 en Python avec un seul appel de fonction : hmac.new(key, msg, hashlib.sha256). Aucune installation pip, aucune extension C, aucune dépendance tierce. Pour des vérifications de signature ponctuelles sans écrire de code, le générateur HMAC en ligne vous donne le résultat instantanément. Ce guide couvre hmac.new(), hmac.digest(), hmac.compare_digest(), l'encodage Base64, la vérification de webhooks, la signature de requêtes API, et tous les algorithmes de hachage de SHA-1 à SHA-512. Tous les exemples ciblent Python 3.7+.

  • hmac.new(key, msg, hashlib.sha256) est le point d'entrée standard — key et msg doivent être des bytes, digestmod est obligatoire depuis Python 3.4.
  • hmac.digest(key, msg, "sha256") est une alternative one-shot plus rapide ajoutée en Python 3.7 — retourne des bytes bruts sans objet intermédiaire.
  • Vérifiez toujours les signatures avec hmac.compare_digest() pour prévenir les attaques temporelles — n'utilisez jamais == pour comparer des signatures HMAC.
  • Encodez en Base64 la sortie brute de .digest() pour les en-têtes HTTP et les signatures webhook : base64.b64encode(h.digest()).
  • Le module hmac accepte tout algorithme hashlib : sha1, sha256, sha384, sha512, md5, blake2b.

Qu'est-ce que HMAC ?

HMAC (Hash-based Message Authentication Code) est une construction définie dans la RFC 2104 qui combine une clé secrète avec une fonction de hachage pour produire un tag d'authentification de taille fixe. Contrairement à un hash simple (que n'importe qui peut calculer), un HMAC nécessite la connaissance de la clé secrète. Cela signifie que vous pouvez l'utiliser pour vérifier à la fois l'intégrité et l'authenticité d'un message. Si même un seul octet du message ou de la clé change, la sortie est complètement différente. La construction fonctionne en hachant la clé XORée avec deux constantes de rembourrage différentes (ipad et opad), encapsulant le message entre deux opérations de hachage. Le module hmac de Python implémente cette RFC directement.

Before · Python
After · Python
# Hash SHA-256 simple — sans clé secrète, tout le monde peut calculer
hashlib.sha256(b"payment:9950:USD").hexdigest()
# "7a3b1c..."  (déterministe, public)
# HMAC-SHA256 — nécessite la clé secrète pour produire
hmac.new(b"api_secret", b"payment:9950:USD", hashlib.sha256).hexdigest()
# "e4f2a8..."  (seul le détenteur de la clé peut calculer)

hmac.new() — Le point d'entrée de la bibliothèque standard

Le module hmac fait partie de la bibliothèque standard Python. Deux imports et vous êtes prêt : import hmac, hashlib. Les trois fonctions principales sont hmac.new() (crée un objet HMAC), hmac.digest() (one-shot, Python 3.7+), et hmac.compare_digest() (comparaison en temps constant). Aucun pip install requis.

hmac.new(key, msg, digestmod) prend trois arguments. Aussi bien key que msg doivent être des objets de type bytes ( bytes, bytearray, ou memoryview). L'argument digestmod est obligatoire depuis Python 3.4 et accepte tout constructeur hashlib (comme hashlib.sha256) ou un nom de chaîne comme "sha256".

Python 3.7+ — exemple minimal HMAC-SHA256
import hmac
import hashlib

key = b"webhook_signing_key_2026"
message = b'{"event":"invoice.paid","invoice_id":"inv_8f3a","amount":19900}'

# Créer l'objet HMAC et obtenir la signature hex
signature = hmac.new(key, message, hashlib.sha256).hexdigest()
print(signature)
# "b4e74f6c9a1d3e5f8b2a7c0d4e6f1a3b5c7d9e0f2a4b6c8d0e1f3a5b7c9d0e2f"

L'objet HMAC expose deux méthodes de sortie. .digest() retourne des bytes bruts (32 octets pour SHA-256, 64 pour SHA-512). .hexdigest() retourne une chaîne hex en minuscules. La chaîne hex est un simple str Python — aucune étape de décodage nécessaire.

Python 3.7+ — .digest() vs .hexdigest()
import hmac
import hashlib

key = b"service_auth_key"
msg = b"GET /api/v2/orders 2026-03-28T14:30:00Z"

h = hmac.new(key, msg, hashlib.sha256)

raw_bytes = h.digest()
print(type(raw_bytes), len(raw_bytes))
# <class 'bytes'> 32

hex_string = h.hexdigest()
print(type(hex_string), len(hex_string))
# <class 'str'> 64

# Ils représentent les mêmes données — hex est juste un encodage en chaîne des bytes
assert raw_bytes.hex() == hex_string

Si votre clé ou message est une chaîne Python, appelez .encode() pour la convertir en bytes avant de la passer à hmac.new(). C'est l'erreur classique que tout le monde fait la première fois — les chaînes Python 3 sont Unicode, pas des bytes, et le module hmac les refuse.

Python 3.7+ — encodage de chaînes en bytes
import hmac
import hashlib

# Clé et message en chaîne — .encode() convertit en bytes UTF-8
api_key = "sk_live_9f3a2b7c4d8e"
request_body = '{"customer_id":"cust_4421","plan":"enterprise"}'

signature = hmac.new(
    api_key.encode(),
    request_body.encode(),
    hashlib.sha256
).hexdigest()

print(signature)
# "3a9f1b..."  — sortie hex cohérente
Note :Le paramètre digestmodn'a pas de valeur par défaut depuis Python 3.4. Appeler hmac.new(key, msg) sans lui lève une TypeError. Avant 3.4, il valait MD5 par défaut, ce qui explique pourquoi les mainteneurs Python ont supprimé cette valeur — vous forçant à faire un choix explicite et sûr.

HMAC-SHA256 Base64, SHA-1, SHA-512 et MD5

La fonction hmac.new() fonctionne avec tout algorithme de hachage disponible dans hashlib. La plupart des fournisseurs de webhooks et passerelles API utilisent HMAC-SHA256, mais vous rencontrerez SHA-1 dans OAuth 1.0a, SHA-512 dans les protocoles qui l'imposent, et MD5 dans les systèmes hérités qui n'ont pas été mis à jour.

HMAC-SHA256 avec sortie Base64

De nombreux fournisseurs de webhooks envoient la signature sous forme de chaîne encodée en Base64 dans un en-tête HTTP. Pour produire le même format, passez les bytes bruts de .digest() à base64.b64encode().

Python 3.7+ — encodage Base64 HMAC-SHA256
import hmac
import hashlib
import base64

key = b"whsec_MbkP7x9yFqHGn3tRdWz5"
payload = b'{"id":"evt_1Nq","type":"charge.succeeded","data":{"amount":4200}}'

# Digest brut → Base64 (courant pour les en-têtes Authorization et les signatures webhook)
raw_digest = hmac.new(key, payload, hashlib.sha256).digest()
b64_signature = base64.b64encode(raw_digest).decode("ascii")

print(b64_signature)
# "dGhpcyBpcyBhIHNhbXBsZSBzaWduYXR1cmU="

# C'est la valeur à comparer avec l'en-tête X-Signature
header_value = f"sha256={b64_signature}"
print(header_value)
# "sha256=dGhpcyBpcyBhIHNhbXBsZSBzaWduYXR1cmU="

HMAC-SHA1 — Compatibilité avec les protocoles hérités

SHA-1 est considéré comme faible pour les nouvelles conceptions, mais HMAC-SHA1 est encore requis par OAuth 1.0a et certaines implémentations de webhooks plus anciennes. Le code est identique — il suffit de changer l'algorithme.

Python 3.7+ — HMAC-SHA1
import hmac
import hashlib

consumer_secret = b"oauth_consumer_secret_2026"
token_secret = b"oauth_token_secret_2026"

# OAuth 1.0a utilise consumer_secret&token_secret comme clé de signature
signing_key = consumer_secret + b"&" + token_secret
base_string = b"GET&https%3A%2F%2Fapi.service.com%2Fv1%2Forders&oauth_nonce%3D7f3a91bc"

sig = hmac.new(signing_key, base_string, hashlib.sha1).digest()

import base64
oauth_signature = base64.b64encode(sig).decode("ascii")
print(oauth_signature)
# "Tza3R9sE..."  — encoder cette valeur en URL pour l'en-tête Authorization

HMAC-SHA512 — Sortie plus longue

Python 3.7+ — HMAC-SHA512
import hmac
import hashlib

key = b"high_security_signing_key_64_bytes_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
msg = b'{"transfer_id":"xfr_9c2e","amount":500000,"currency":"EUR"}'

h = hmac.new(key, msg, hashlib.sha512)

print(len(h.digest()))   # 64 octets (512 bits)
print(len(h.hexdigest()))  # 128 caractères hex
print(h.hexdigest()[:40] + "...")
# "8e3a1f9b2c4d6e7f0a1b3c5d7e9f0a2b4c6d8e0f..."

HMAC-MD5 — Usage hérité uniquement

Python 3.7+ — HMAC-MD5
import hmac
import hashlib

# MD5 est cryptographiquement compromis — à n'utiliser que pour la compatibilité avec les protocoles hérités
key = b"legacy_api_key"
msg = b"action=charge&amount=1500&merchant=store_42"

sig = hmac.new(key, msg, hashlib.md5).hexdigest()
print(sig)  # chaîne hex de 32 caractères
# "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
Avertissement :HMAC-MD5 n'est acceptable que pour la compatibilité ascendante avec des systèmes que vous ne pouvez pas migrer. Pour tout nouveau projet, utilisez au minimum HMAC-SHA256. MD5 présente des vulnérabilités de collision connues qui, bien que moins directement exploitables en mode HMAC, en font un mauvais choix par défaut.

Référence des paramètres hmac.new()

La signature du constructeur est hmac.new(key, msg=None, digestmod). Les trois fonctions du module partagent le même schéma pour les arguments de clé et d'algorithme.

Constructeur hmac.new()

Paramètre
Type
Défaut
Description
key
bytes | bytearray
(requis)
La clé secrète — doit être en bytes, pas une chaîne
msg
bytes | None
None
Message initial à hacher ; on peut ajouter des données via .update()
digestmod
str | callable
(requis)
Algorithme de hachage — ex. hashlib.sha256 ou la chaîne "sha256"

hmac.digest() one-shot (Python 3.7+)

Paramètre
Type
Description
key
bytes
La clé secrète
msg
bytes
Le message à authentifier
digest
str | callable
Algorithme de hachage — identique à digestmod dans hmac.new()

Le paramètre digestmod accepte soit un callable (comme hashlib.sha256) soit un nom de chaîne (comme "sha256"). La forme callable est préférée car elle est validée à l'importation — une faute de frappe dans la forme chaîne ne provoque une erreur qu'à l'exécution.

hmac.digest() — HMAC one-shot rapide (Python 3.7+)

Python 3.7 a ajouté hmac.digest(key, msg, digest) comme fonction de niveau module. Elle calcule le HMAC en un seul appel sans créer d'objet HMAC intermédiaire. La valeur de retour est des bytes bruts (équivalent à appeler .digest() sur l'objet). Cette fonction utilise une implémentation C optimisée sur CPython et évite l'overhead d'allocation d'objet, ce qui la rend sensiblement plus rapide dans les boucles serrées.

Python 3.7+ — hmac.digest() one-shot
import hmac
import hashlib

key = b"batch_signing_key_2026"
messages = [
    b'{"order_id":"ord_001","total":4500}',
    b'{"order_id":"ord_002","total":8900}',
    b'{"order_id":"ord_003","total":2200}',
]

# Digest one-shot — sans objet HMAC intermédiaire
signatures = [hmac.digest(key, msg, hashlib.sha256) for msg in messages]

# Conversion en hex pour l'affichage
for msg, sig in zip(messages, signatures):
    print(f"{msg[:30]}... -> {sig.hex()[:24]}...")

La limitation : hmac.digest() ne retourne que des bytes bruts. Si vous avez besoin directement de la chaîne hex, vous devez toujours utiliser hmac.new() pour sa méthode .hexdigest(), ou chaîner .hex() sur le résultat bytes.

Note :hmac.digest() ne prend pas en charge les appels .update() incrémentiels. Si vous lisez un grand fichier par morceaux et devez calculer un HMAC sur son contenu, restez sur hmac.new() et appelez .update(chunk) dans une boucle.

Vérifier une signature HMAC depuis un webhook ou une réponse API

L'utilisation la plus courante de HMAC en Python est la vérification des signatures webhook. Tous les grands fournisseurs (Stripe, GitHub, Shopify, Twilio) signent les payloads avec HMAC-SHA256 et envoient la signature dans un en-tête. Le schéma est toujours le même : recalculer le HMAC sur le corps brut de la requête, puis comparer avec hmac.compare_digest().

Vérification de signature webhook

Python 3.7+ — vérification HMAC webhook (Flask)
import hmac
import hashlib
from flask import Flask, request, abort

app = Flask(__name__)
WEBHOOK_SECRET = b"whsec_MbkP7x9yFqHGn3tRdWz5"

@app.route("/webhooks/payments", methods=["POST"])
def handle_payment_webhook():
    # Obtenir le corps brut — doit correspondre exactement à ce qui a été signé
    raw_body = request.get_data()

    # Récupérer la signature depuis l'en-tête
    received_sig = request.headers.get("X-Signature-256", "")

    # Recalculer le HMAC sur le corps brut
    expected_sig = hmac.new(WEBHOOK_SECRET, raw_body, hashlib.sha256).hexdigest()

    # Comparaison en temps constant — prévient les attaques temporelles
    if not hmac.compare_digest(f"sha256={expected_sig}", received_sig):
        abort(403, "Invalid signature")

    # Signature vérifiée — traitement de l'événement
    event = request.get_json()
    print(f"Verified event: {event['type']} for {event['data']['amount']}")
    return "", 200

La fonction hmac.compare_digest() compare deux chaînes ou séquences d'octets en temps constant. Une comparaison classique == court-circuite dès le premier octet non correspondant. Un attaquant peut mesurer le temps de réponse sur de nombreuses requêtes et reconstruire progressivement la signature attendue octet par octet. La comparaison en temps constant élimine ce canal auxiliaire.

Vérification de webhook GitHub

Le format de webhook de GitHub illustre le schéma complet. Il envoie un en-tête X-Hub-Signature-256 contenant sha256= suivi du HMAC-SHA256 encodé en hex du corps brut de la requête, signé avec le secret webhook que vous configurez dans les paramètres de votre dépôt. La différence clé par rapport à une vérification webhook générique est que vous devez supprimer le préfixe sha256= avant de comparer, et vous devez lire les bytes brutsdu corps de la requête — analyser le JSON d'abord change la représentation en octets et casse la vérification.

Python 3.7+ — vérification GitHub X-Hub-Signature-256
import hmac
import hashlib
from flask import Flask, request, abort

app = Flask(__name__)
GITHUB_WEBHOOK_SECRET = b"your_github_webhook_secret"

@app.route("/webhooks/github", methods=["POST"])
def handle_github_webhook():
    # GitHub envoie : X-Hub-Signature-256: sha256=<hex_digest>
    sig_header = request.headers.get("X-Hub-Signature-256", "")

    if not sig_header.startswith("sha256="):
        abort(403, "Missing or malformed signature header")

    received_hex = sig_header[len("sha256="):]
    raw_body = request.get_data()  # bytes bruts — ne pas analyser JSON avant

    expected_hex = hmac.new(
        GITHUB_WEBHOOK_SECRET, raw_body, hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(expected_hex, received_hex):
        abort(403, "Signature mismatch — payload may have been tampered with")

    event_type = request.headers.get("X-GitHub-Event", "unknown")
    payload = request.get_json()
    print(f"Verified GitHub {event_type} event: {payload.get('action', '')}")
    return "", 200

Le même schéma s'applique à Shopify (X-Shopify-Hmac-SHA256) et Twilio (X-Twilio-Signature), la seule différence étant le nom de l'en-tête et si la signature est en hex ou encodée en Base64. Consultez toujours la documentation du fournisseur pour confirmer le format d'encodage — mélanger hex et Base64 est la cause la plus courante d'erreurs de signature.

Authentification de requêtes API avec HMAC

Certaines API demandent au client de signer chaque requête avec HMAC. La chaîne signée inclut généralement la méthode HTTP, le chemin, un timestamp et le corps de la requête. Voici un schéma que j'utilise pour l'authentification inter-services.

Python 3.7+ — signature de requêtes API avec HMAC-SHA256
import hmac
import hashlib
import time
import json

def sign_request(secret: bytes, method: str, path: str, body: str) -> dict:
    """Génère une signature HMAC-SHA256 pour une requête API."""
    timestamp = str(int(time.time()))

    # Construction de la chaîne de signature — méthode + chemin + timestamp + corps
    signing_string = f"{method}\n{path}\n{timestamp}\n{body}"

    signature = hmac.new(
        secret,
        signing_string.encode(),
        hashlib.sha256
    ).hexdigest()

    return {
        "X-Timestamp": timestamp,
        "X-Signature": signature,
    }

# Utilisation
api_secret = b"sk_hmac_9f3a2b7c4d8e1a6f"
body = json.dumps({"customer_id": "cust_4421", "action": "suspend_account"})

headers = sign_request(api_secret, "POST", "/api/v2/customers/actions", body)
print(headers)
# {"X-Timestamp": "1711612200", "X-Signature": "a3f1b9c0..."}

Signature de requêtes HTTP avec la bibliothèque requests

Python 3.7+ — requêtes signées HMAC avec la bibliothèque requests
import hmac
import hashlib
import time
import json
import requests

API_SECRET = b"sk_hmac_9f3a2b7c4d8e1a6f"
BASE_URL = "https://api.billing-service.internal"

def make_signed_request(method: str, path: str, payload: dict) -> requests.Response:
    body = json.dumps(payload, separators=(",", ":"))  # JSON compact, déterministe
    timestamp = str(int(time.time()))

    signing_string = f"{method}\n{path}\n{timestamp}\n{body}"
    signature = hmac.new(API_SECRET, signing_string.encode(), hashlib.sha256).hexdigest()

    headers = {
        "Content-Type": "application/json",
        "X-Timestamp": timestamp,
        "X-Signature": f"hmac-sha256={signature}",
    }

    try:
        return requests.request(method, f"{BASE_URL}{path}", data=body, headers=headers)
    except requests.RequestException as e:
        raise RuntimeError(f"Signed request failed: {e}") from e

# Envoi d'une requête POST signée
resp = make_signed_request("POST", "/api/v2/invoices", {
    "customer_id": "cust_4421",
    "line_items": [
        {"description": "Pro plan - March 2026", "amount": 4900},
        {"description": "Extra seats (3)", "amount": 2100},
    ],
})
print(resp.status_code, resp.json())

Note rapide : utilisez separators=(",", ":") lors de la sérialisation du corps pour la signature. Le json.dumps() par défaut ajoute des espaces après les séparateurs, ce qui change la représentation en octets et casse la vérification de signature si le serveur sérialise différemment. Le JSON compact donne une forme canonique.

Génération HMAC en ligne de commande

Parfois, vous devez calculer un HMAC sans écrire de script. L'option -c de Python et openssl gèrent cela depuis le terminal.

bash — HMAC-SHA256 via Python one-liner
python3 -c "
import hmac, hashlib
print(hmac.new(b'my_secret', b'message_to_sign', hashlib.sha256).hexdigest())
"
# affiche : chaîne hex de 64 caractères
bash — HMAC-SHA256 via openssl (pour comparaison)
echo -n "message_to_sign" | openssl dgst -sha256 -hmac "my_secret"
# SHA2-256(stdin)= 7d11...

# Passer un fichier via HMAC openssl
openssl dgst -sha256 -hmac "my_secret" < payload.json
bash — HMAC depuis une variable d'environnement
# Stocker la clé dans une variable d'env pour éviter l'exposition dans l'historique shell
export HMAC_KEY="sk_live_9f3a2b"
echo -n '{"event":"test"}' | python3 -c "
import hmac, hashlib, sys, os
key = os.environ['HMAC_KEY'].encode()
msg = sys.stdin.buffer.read()
print(hmac.new(key, msg, hashlib.sha256).hexdigest())
"
Note :L'option echo -nest indispensable — sans elle, echo ajoute un caractère de saut de ligne au message, ce qui change la sortie HMAC. C'est la cause la plus courante d'erreur de signature lors du débogage depuis le terminal.

Alternative haute performance — bibliothèque cryptography

Pour la plupart des applications, le module hmac standard est suffisamment rapide. Si vous utilisez déjà la bibliothèque cryptography pour TLS ou la gestion des certificats, elle fournit également HMAC basé sur OpenSSL. La principale différence pratique par rapport à la stdlib est l'API .verify() basée sur les exceptions décrite ci-dessous — elle lève une exception en cas d'erreur au lieu de retourner un booléen que vous pourriez oublier de vérifier.

bash — installation de cryptography
pip install cryptography
Python 3.7+ — HMAC via la bibliothèque cryptography
from cryptography.hazmat.primitives import hashes, hmac as crypto_hmac

key = b"webhook_signing_key_2026"
message = b'{"event":"subscription.renewed","plan":"enterprise"}'

h = crypto_hmac.HMAC(key, hashes.SHA256())
h.update(message)
signature = h.finalize()  # bytes bruts

print(signature.hex())
# "9c4e2a..."

# Mode vérification — lève InvalidSignature en cas d'erreur
h_verify = crypto_hmac.HMAC(key, hashes.SHA256())
h_verify.update(message)
h_verify.verify(signature)  # lève cryptography.exceptions.InvalidSignature si incorrect

La méthode .verify() de la bibliothèque cryptography est particulièrement pratique : elle lève une exception en cas d'erreur au lieu de retourner un booléen. Cela rend plus difficile d'ignorer accidentellement un échec de vérification. Le hmac.compare_digest() de la bibliothèque standard retourne True/ False, ce qui peut être ignoré silencieusement si le développeur oublie de vérifier la valeur de retour.

Affichage coloré dans le terminal

Si vous déboguez des signatures HMAC dans le terminal et souhaitez une sortie colorée, rich gère cela très bien.

bash — installation de rich
pip install rich
Python 3.7+ — sortie HMAC colorée avec rich
import hmac
import hashlib
from rich.console import Console
from rich.table import Table

console = Console()

key = b"debug_signing_key"
messages = {
    "/api/v2/orders": b'{"status":"active"}',
    "/api/v2/invoices": b'{"status":"pending"}',
    "/api/v2/customers": b'{"status":"verified"}',
}

table = Table(title="HMAC-SHA256 Signatures")
table.add_column("Endpoint", style="cyan")
table.add_column("Signature (32 premiers caractères)", style="green")

for endpoint, body in messages.items():
    sig = hmac.new(key, body, hashlib.sha256).hexdigest()
    table.add_row(endpoint, sig[:32] + "...")

console.print(table)
Note :Rich est réservé à l'affichage dans le terminal. Ne l'utilisez pas pour écrire des signatures HMAC dans des fichiers, des en-têtes HTTP ou des systèmes d'agrégation de logs — les codes d'échappement ANSI corrompront la sortie.

Travail avec les gros fichiers — HMAC incrémental

Pour les fichiers de plus de 50 Mo environ, charger tout en mémoire uniquement pour calculer un HMAC est inutile. La méthode .update() de l'objet HMAC vous permet d'alimenter les données par morceaux. Cela maintient l'utilisation mémoire constante quelle que soit la taille du fichier.

Python 3.7+ — HMAC d'un grand fichier par morceaux
import hmac
import hashlib

def hmac_file(key: bytes, filepath: str, chunk_size: int = 8192) -> str:
    """Calcule le HMAC-SHA256 d'un fichier sans le charger entièrement en mémoire."""
    h = hmac.new(key, digestmod=hashlib.sha256)

    try:
        with open(filepath, "rb") as f:
            while True:
                chunk = f.read(chunk_size)
                if not chunk:
                    break
                h.update(chunk)
    except OSError as e:
        raise OSError(f"Cannot read file '{filepath}': {e}") from e

    return h.hexdigest()

# Signer un export de base de données de 2 Go
signing_key = b"backup_integrity_key_2026"
signature = hmac_file(signing_key, "/var/backups/db-export-2026-03.sql.gz")
print(f"HMAC-SHA256: {signature}")
Python 3.7+ — vérification de la signature HMAC d'un fichier téléchargé
import hmac
import hashlib

def verify_file_hmac(key: bytes, filepath: str, expected_hex: str) -> bool:
    """Vérifie la signature HMAC-SHA256 d'un fichier."""
    h = hmac.new(key, digestmod=hashlib.sha256)

    with open(filepath, "rb") as f:
        for chunk in iter(lambda: f.read(65536), b""):
            h.update(chunk)

    return hmac.compare_digest(h.hexdigest(), expected_hex)

# Vérification d'un artefact téléchargé
is_valid = verify_file_hmac(
    key=b"release_signing_key",
    filepath="/tmp/release-v3.2.0.tar.gz",
    expected_hex="8e3a1f9b2c4d6e7f0a1b3c5d7e9f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f",
)
print(f"Intégrité du fichier : {'valide' if is_valid else 'CORROMPU'}")
Note :Passez au HMAC par morceaux lorsque le fichier dépasse 50-100 Mo ou lors du traitement d'uploads sur un serveur web où la mémoire par requête est limitée. L'approche .update() utilise un chunk_sizefixe en mémoire quelle que soit la taille du fichier. Je choisis par défaut des morceaux de 64 Ko — assez grands pour amortir l'overhead des appels système, assez petits pour rester dans le cache L2 de la plupart des matériels.

Générer une clé HMAC cryptographiquement sécurisée en Python

Une clé faible ou prévisible compromet toute la construction HMAC. Le module secrets (Python 3.6+) fournit des bytes aléatoires cryptographiquement solides. Pour HMAC-SHA256, utilisez une clé de 32 octets. Pour HMAC-SHA512, utilisez 64 octets. Ces tailles correspondent à la taille de bloc interne des algorithmes de hachage respectifs, ce qui est la longueur de clé optimale selon la RFC 2104.

Python 3.7+ — génération de clés HMAC avec secrets
import secrets

# Génération de clés correspondant à la taille de bloc de l'algorithme
sha256_key = secrets.token_bytes(32)   # 256 bits — pour HMAC-SHA256
sha512_key = secrets.token_bytes(64)   # 512 bits — pour HMAC-SHA512

# Représentation hex — sûre pour les fichiers de configuration et les variables d'env
print(f"Clé HMAC-SHA256 : {sha256_key.hex()}")
# ex. "a3f1b9c04e7d2f8a1b3c5d7e9f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f"

print(f"Clé HMAC-SHA512 : {sha512_key.hex()}")
# chaîne hex de 128 caractères

# Base64 URL-safe — compact, sûr pour les en-têtes HTTP
import base64
b64_key = base64.urlsafe_b64encode(sha256_key).decode("ascii")
print(f"Clé Base64 : {b64_key}")
# ex. "o_G5wE59L4obPF1-nwortG2ODwobPExdXnqLnA0dLi8="
Avertissement :N'utilisez jamais random.random() ni random.randbytes() du module random pour les clés HMAC. Le module random par défaut utilise le PRNG Mersenne Twister, qui est prévisible après avoir observé 624 sorties. Utilisez toujours secrets.token_bytes() pour la génération aléatoire sensible à la sécurité.

Longueur de clé et exigences de la RFC 2104

La RFC 2104 précise que la clé HMAC peut être de n'importe quelle longueur, mais recommande une clé d'au moins L octets — où L est la longueur de sortie de la fonction de hachage sous-jacente. Pour HMAC-SHA256, c'est 32 octets (256 bits). Les clés plus courtes que L bits réduisent la marge de sécurité proportionnellement. Les clés plus longues que la taille de bloc du hachage (64 octets pour SHA-256, 128 octets pour SHA-512) sont d'abord hachées jusqu'à la taille de bloc, donc il n'y a aucun avantage à utiliser des clés plus longues que la taille de bloc. Restez à 32 octets pour HMAC-SHA256 et 64 octets pour HMAC-SHA512.

Stockage et rotation des clés

Ne codez jamais en dur les clés HMAC dans le code source. L'approche standard pour les déploiements en production est de charger la clé depuis une variable d'environnement au démarrage : os.environ["HMAC_SECRET"].encode(). Pour des environnements à haute assurance, stockez les clés dans un système de gestion de secrets comme AWS Secrets Manager, HashiCorp Vault ou GCP Secret Manager et récupérez-les à l'exécution. Ces systèmes fournissent des journaux d'audit, des contrôles d'accès et une rotation automatique sans nécessiter un déploiement de code.

Planifiez la rotation des clés dès le départ. Lors d'une rotation, il existe une fenêtre pendant laquelle les requêtes en transit ont été signées avec l'ancienne clé et échoueront la vérification contre la nouvelle. La mitigation standard est une courte période de chevauchement : accepter les signatures de l'ancienne et de la nouvelle clé pendant un court moment (minutes à heures), puis retirer l'ancienne clé. Si une clé est compromise — exposée dans des logs, divulguée via un commit git ou lors d'un incident — faites pivoter immédiatement et traitez toutes les signatures produites avec la clé compromise comme non fiables. Revérifiez les résultats de vérification mis en cache et notifiez les consommateurs en aval du changement de clé.

Utilisation de bytearray et memoryview avec hmac.new()

La fonction hmac.new() accepte tout objet de type bytes pour les paramètres key et msg. Cela signifie que vous pouvez passer bytes, bytearray, ou memoryview directement, sans copie ni conversion. Cela importe surtout dans deux scénarios : les implémentations de protocoles réseau où socket.recv_into() écrit les données dans un buffer bytearray pré-alloué, et les systèmes à haut débit où éviter les copies intermédiaires réduit la pression sur le GC. Une tranche memoryview est sans copie : elle expose une fenêtre sur le buffer original sans allouer de nouvelle mémoire. À des dizaines de milliers de messages par seconde, éliminer ces allocations fait une différence mesurable en latence et en débit.

Python 3.7+ — bytearray et memoryview avec HMAC
import hmac
import hashlib

# bytearray — bytes mutables, utile pour les buffers de protocole binaire
key = bytearray(b"protocol_signing_key")
frame = bytearray(b'\x01\x02\x03\x04payload_data_here')

sig = hmac.new(key, frame, hashlib.sha256).hexdigest()
print(f"Signature de trame : {sig[:32]}...")

# memoryview — tranche sans copie d'un buffer plus grand
large_buffer = bytearray(4096)
large_buffer[:20] = b"sensor_reading_12345"

# HMAC uniquement sur les 20 premiers octets sans copie
view = memoryview(large_buffer)[:20]
sig = hmac.new(key, view, hashlib.sha256).hexdigest()
print(f"Signature capteur : {sig[:32]}...")

Erreurs courantes

Je vois les deux premières erreurs dans presque chaque revue de code impliquant des handlers webhook. Elles sont faciles à introduire sous la pression du temps et difficiles à repérer sans savoir quoi chercher.

Comparer les signatures HMAC avec == au lieu de hmac.compare_digest()

Problème : L'opérateur == court-circuite dès le premier octet non correspondant, révélant des informations temporelles qui permettent à un attaquant de reconstruire la signature attendue de manière incrémentielle.

Correction : Utilisez toujours hmac.compare_digest() pour la comparaison de signatures — il s'exécute en temps constant quel que soit l'endroit où la divergence se produit.

Before · Python
After · Python
received_sig = request.headers["X-Signature"]
expected_sig = hmac.new(key, body, hashlib.sha256).hexdigest()

if received_sig == expected_sig:  # VULNÉRABLE à l'attaque temporelle
    process_webhook(body)
received_sig = request.headers["X-Signature"]
expected_sig = hmac.new(key, body, hashlib.sha256).hexdigest()

if hmac.compare_digest(received_sig, expected_sig):  # temps constant
    process_webhook(body)
Passer une chaîne au lieu de bytes à hmac.new()

Problème : hmac.new() nécessite des objets de type bytes. Passer un Python str lève TypeError: "key: expected bytes or bytearray, but got 'str'".

Correction : Appelez .encode() sur les clés et messages en chaîne avant de les passer à hmac.new().

Before · Python
After · Python
key = "my_api_secret"  # str, pas des bytes
msg = '{"event":"test"}'  # str, pas des bytes
sig = hmac.new(key, msg, hashlib.sha256)  # TypeError !
key = "my_api_secret"
msg = '{"event":"test"}'
sig = hmac.new(key.encode(), msg.encode(), hashlib.sha256)
Oublier digestmod (Python 3.4+)

Problème : Appeler hmac.new(key, msg) sans le troisième argument lève TypeError. Avant Python 3.4, il valait MD5 par défaut, mais la valeur par défaut a été supprimée pour des raisons de sécurité.

Correction : Passez toujours l'algorithme explicitement : hashlib.sha256, hashlib.sha512, ou ce que votre protocole requiert.

Before · Python
After · Python
# digestmod manquant — lève TypeError en Python 3.4+
sig = hmac.new(key, msg).hexdigest()
# Toujours spécifier l'algorithme de hachage
sig = hmac.new(key, msg, hashlib.sha256).hexdigest()
Utiliser .hexdigest() quand le fournisseur attend du Base64

Problème : De nombreux fournisseurs de webhooks (Stripe, Shopify) envoient des signatures encodées en Base64, pas en hex. Comparer une chaîne hex avec une valeur Base64 échoue toujours, causant le rejet de tous les webhooks.

Correction : Vérifiez la documentation du fournisseur pour le format de signature. S'il utilise Base64, encodez les bytes bruts de .digest(), pas la chaîne .hexdigest().

Before · Python
After · Python
# Le fournisseur envoie Base64, mais on calcule hex — ne correspond jamais
expected = hmac.new(key, body, hashlib.sha256).hexdigest()
# "a3f1b9c0..."  vs  "o/G5wE59..."  — toujours différent
import base64
# Correspondre au format du fournisseur : bytes bruts → Base64
raw = hmac.new(key, body, hashlib.sha256).digest()
expected = base64.b64encode(raw).decode("ascii")
# "o/G5wE59..."  — correspond à l'en-tête

stdlib hmac vs cryptography — Comparaison rapide

Méthode
Algorithme
Sortie
Streaming
Types personnalisés
Installation requise
hmac.new() + hexdigest()
Tout hashlib
Chaîne hex
✓ via .update()
N/A
Non (stdlib)
hmac.new() + digest()
Tout hashlib
Bytes bruts
✓ via .update()
N/A
Non (stdlib)
hmac.digest()
Tout hashlib
Bytes bruts
✗ (one-shot)
N/A
Non (stdlib, 3.7+)
hashlib.sha256 (hash simple)
SHA-256 uniquement
Hex ou bytes
✓ via .update()
N/A
Non (stdlib)
cryptography (HMAC)
Tout
Bytes bruts
✓ via .update()
N/A
pip install
pyca/cryptography + CMAC
AES-CMAC
Bytes bruts
✓ via .update()
N/A
pip install

Utilisez le module hmac de la stdlib pour la vérification de webhooks, la signature d'API et les opérations HMAC générales — il ne nécessite aucune dépendance et couvre tous les algorithmes standards. Utilisez hmac.digest() pour les opérations par lots où la rapidité one-shot compte. Tournez-vous vers la bibliothèque cryptography uniquement si vous en dépendez déjà pour d'autres opérations (TLS, X.509, chiffrement symétrique) et voulez l'API .verify() basée sur les exceptions. Pour des vérifications rapides de signature sans écrire de Python, utilisez le générateur HMAC pour coller votre clé et votre message et obtenir le résultat instantanément.

Questions fréquentes

Quelle est la différence entre hmac.new() et hmac.digest() en Python ?

hmac.new() retourne un objet HMAC qui prend en charge les appels .update() incrémentiels et fournit à la fois .digest() (bytes bruts) et .hexdigest() (chaîne hex). hmac.digest() est une fonction one-shot ajoutée en Python 3.7 qui retourne directement des bytes bruts sans créer d'objet intermédiaire. Utilisez hmac.digest() lorsque vous disposez du message complet et n'avez besoin que du résultat. Utilisez hmac.new() lorsque vous devez alimenter les données par morceaux ou avez besoin de la sortie hex.

Python
import hmac, hashlib

key = b"webhook_secret_2026"
body = b'{"event":"payment.completed","amount":9950}'

# One-shot — retourne des bytes bruts
raw = hmac.digest(key, body, hashlib.sha256)

# Basé sur un objet — prend en charge les mises à jour incrémentielles et la sortie hex
h = hmac.new(key, body, hashlib.sha256)
hex_sig = h.hexdigest()

Comment vérifier une signature HMAC en Python ?

Recalculez le HMAC sur le message original en utilisant le secret partagé, puis comparez avec hmac.compare_digest(). N'utilisez jamais == pour la comparaison de signatures. L'opérateur == est vulnérable aux attaques temporelles car il court-circuite dès le premier octet non correspondant, révélant des informations sur la longueur et le contenu de la signature attendue.

Python
import hmac, hashlib

def verify_signature(secret: bytes, message: bytes, received_sig: str) -> bool:
    expected = hmac.new(secret, message, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, received_sig)

Python hmac SHA-256 est-il identique à un hachage avec hashlib.sha256 ?

Non. hashlib.sha256 calcule un simple hachage SHA-256 de l'entrée, que n'importe qui peut reproduire. HMAC-SHA256 intègre une clé secrète dans le calcul de hachage selon la RFC 2104, de sorte que seul celui qui possède la clé peut produire ou vérifier la sortie correcte. Un hash simple prouve l'intégrité des données. Un HMAC prouve à la fois l'intégrité et l'authenticité.

Python
import hmac, hashlib

msg = b"transfer:9950:USD"
key = b"api_secret_k8x2"

plain_hash = hashlib.sha256(msg).hexdigest()  # n'importe qui peut calculer ça
hmac_hash = hmac.new(key, msg, hashlib.sha256).hexdigest()  # nécessite la clé

Puis-je utiliser HMAC-SHA1 en Python 3 ?

Oui, passez hashlib.sha1 comme argument digestmod. HMAC-SHA1 fonctionne toujours en Python 3 et le module hmac n'émet aucun avertissement de dépréciation à ce sujet. Cela dit, SHA-1 est considéré comme faible pour les nouvelles conceptions — sa résistance aux collisions est inférieure à 80 bits et le NIST l'a déprécié pour la plupart des usages de signature numérique en 2015. La principale raison d'utiliser HMAC-SHA1 aujourd'hui est la compatibilité ascendante avec des protocoles existants qui l'imposent, comme OAuth 1.0a ou certains systèmes de webhooks hérités. Quand vous contrôlez les deux côtés de l'intégration, préférez HMAC-SHA256 ou HMAC-SHA512 pour tout nouveau développement.

Python
import hmac, hashlib

key = b"oauth_consumer_secret"
base_string = b"GET&https%3A%2F%2Fapi.example.com&oauth_nonce%3Dabc123"
sig = hmac.new(key, base_string, hashlib.sha1).digest()

Comment générer une clé HMAC sécurisée en Python ?

Utilisez secrets.token_bytes() de la bibliothèque standard. Pour HMAC-SHA256, une clé de 32 octets est la recommandation standard car elle correspond à la taille de bloc du hachage. Pour HMAC-SHA512, utilisez 64 octets. N'utilisez pas random.random() ni os.urandom() pour la génération de clés dans le code applicatif — secrets est le module approprié pour la génération aléatoire sensible à la sécurité depuis Python 3.6.

Python
import secrets

hmac_sha256_key = secrets.token_bytes(32)  # 256 bits
hmac_sha512_key = secrets.token_bytes(64)  # 512 bits

# Stockage en hex pour les fichiers de configuration
print(hmac_sha256_key.hex())
# ex. "a3f1b9c04e..."

Pourquoi hmac.new() exige-t-il digestmod en Python 3 ?

Avant Python 3.4, digestmod valait MD5 par défaut, un algorithme cryptographiquement compromis — MD5 présente des attaques par collision connues et ne devrait jamais être utilisé dans du nouveau code sensible à la sécurité. Les mainteneurs Python ont supprimé cette valeur par défaut pour forcer un choix d'algorithme explicite, empêchant ainsi les développeurs de livrer silencieusement des MAC basés sur MD5. Si vous appelez hmac.new(key, msg) sans digestmod, vous obtenez une TypeError. Passez toujours l'algorithme explicitement : hashlib.sha256, hashlib.sha512, ou tout autre constructeur hashlib. En cas de doute, hashlib.sha256 est le choix sûr par défaut — aucune faiblesse connue et suffisamment rapide pour toute charge de travail pratique.

Python
import hmac, hashlib

key = b"secret"
msg = b"data"

# Ceci lève TypeError en Python 3.4+
# hmac.new(key, msg)  # TypeError: missing required argument: 'digestmod'

# Toujours spécifier l'algorithme
h = hmac.new(key, msg, hashlib.sha256)

Pour une vérification HMAC rapide sans lancer un script Python, collez votre clé et votre message dans le générateur HMAC en ligne — il prend en charge SHA-256, SHA-384 et SHA-512 avec des résultats instantanés.

Outils associés

DV
Dmitri VolkovDevOps Engineer & Python Automation Specialist

Dmitri is a DevOps engineer who relies on Python as his primary scripting and automation language. He builds internal tooling, CI/CD pipelines, and infrastructure automation scripts that run in production across distributed teams. He writes about the Python standard library, subprocess management, file processing, encoding utilities, and the practical shell-adjacent Python that DevOps engineers use every day.

MS
Maria SantosRéviseur technique

Maria is a backend developer specialising in Python and API integration. She has broad experience with data pipelines, serialisation formats, and building reliable server-side services. She is an active member of the Python community and enjoys writing practical, example-driven guides that help developers solve real problems without unnecessary theory.