HMAC Python — hmac.new() SHA-256 Gids + Code-voorbeelden
Gebruik de gratis HMAC Generator direct in je browser — geen installatie nodig.
HMAC Generator online uitproberen →Elke webhook-callback, elk ondertekend API-verzoek, elke Stripe- of GitHub-eventmelding gebruikt een HMAC-handtekeningom te bewijzen dat de payload niet is gemanipuleerd. Python's hmac module verwerkt HMAC-SHA256 in Python met één functieaanroep: hmac.new(key, msg, hashlib.sha256). Geen pip install, geen C-extensie, geen externe afhankelijkheid. Voor snelle eenmalige handtekeningcontroles zonder code te schrijven geeft de online HMAC Generator het resultaat direct. Deze gids behandelt hmac.new(), hmac.digest(), hmac.compare_digest(), Base64-codering, webhook-verificatie, API-verzoekondertekening en elk hash-algoritme van SHA-1 tot SHA-512. Alle voorbeelden zijn gericht op Python 3.7+.
- ✓hmac.new(key, msg, hashlib.sha256) is het standaard startpunt — key en msg moeten bytes zijn, digestmod is verplicht sinds Python 3.4.
- ✓hmac.digest(key, msg, "sha256") is een sneller eenmalig alternatief toegevoegd in Python 3.7 — geeft ruwe bytes terug, geen tussenliggend object.
- ✓Verifieer handtekeningen altijd met hmac.compare_digest() om timing-aanvallen te voorkomen — gebruik nooit == voor HMAC-vergelijking.
- ✓Codeer de ruwe .digest()-uitvoer naar Base64 voor HTTP-headers en webhook-handtekeningen: base64.b64encode(h.digest()).
- ✓De hmac-module accepteert elk hashlib-algoritme: sha1, sha256, sha384, sha512, md5, blake2b.
Wat is HMAC?
HMAC (Hash-based Message Authentication Code) is een constructie gedefinieerd in RFC 2104 die een geheime sleutel combineert met een hashfunctie om een authenticatietag van vaste lengte te produceren. Anders dan een gewone hash (die iedereen kan berekenen) vereist een HMAC kennis van de geheime sleutel. Hiermee kun je zowel de integriteit als de authenticiteit van een bericht verifiëren. Als zelfs één byte van het bericht of de sleutel verandert, is de uitvoer volledig anders. De constructie werkt door de sleutel te XOR-en met twee verschillende opvulconstanten (ipad en opad) en het bericht in te pakken tussen twee hashbewerkingen. Python's hmac module implementeert dit RFC direct.
# Gewone SHA-256-hash — geen geheime sleutel, iedereen kan dit berekenen hashlib.sha256(b"payment:9950:USD").hexdigest() # "7a3b1c..." (deterministisch, openbaar)
# HMAC-SHA256 — vereist de geheime sleutel om te produceren hmac.new(b"api_secret", b"payment:9950:USD", hashlib.sha256).hexdigest() # "e4f2a8..." (alleen de sleutelhouder kan dit berekenen)
hmac.new() — Het standaardbibliotheek startpunt
De hmac module maakt deel uit van de Python-standaardbibliotheek. Twee imports en je bent klaar: import hmac, hashlib. De drie hoofdfuncties zijn hmac.new() (maakt een HMAC-object aan), hmac.digest() (eenmalig, Python 3.7+) en hmac.compare_digest() (constante-tijdvergelijking). Geen pip install vereist.
hmac.new(key, msg, digestmod) accepteert drie argumenten. Zowel key als msg moeten bytes-achtige objecten zijn ( bytes, bytearray of memoryview). Het argument digestmod is verplicht sinds Python 3.4 en accepteert elke hashlib constructor (zoals hashlib.sha256) of een stringnaam zoals "sha256".
import hmac
import hashlib
key = b"webhook_signing_key_2026"
message = b'{"event":"invoice.paid","invoice_id":"inv_8f3a","amount":19900}'
# Maak het HMAC-object aan en haal de hex-handtekening op
signature = hmac.new(key, message, hashlib.sha256).hexdigest()
print(signature)
# "b4e74f6c9a1d3e5f8b2a7c0d4e6f1a3b5c7d9e0f2a4b6c8d0e1f3a5b7c9d0e2f"Het HMAC-object biedt twee uitvoermethoden. .digest() geeft ruwe bytes terug (32 bytes voor SHA-256, 64 voor SHA-512). .hexdigest() geeft een kleine-letter hex-string terug. De hex-string is een gewone Python str — geen decoderingstap nodig.
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 # Ze vertegenwoordigen dezelfde data — hex is slechts een stringcodering van de bytes assert raw_bytes.hex() == hex_string
Als je sleutel of bericht een Python-string is, roep dan .encode() aan om het naar bytes te converteren voordat je het doorgeeft aan hmac.new(). Dit gaat bijna iedereen de eerste keer fout — Python 3-strings zijn Unicode, niet bytes, en de hmac-module weigert ze.
import hmac
import hashlib
# String-sleutel en -bericht — .encode() converteert naar UTF-8-bytes
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..." — consistente hex-string-uitvoerdigestmod heeft geen standaardwaarde sinds Python 3.4. Het aanroepen van hmac.new(key, msg) zonder digestmod geeft een TypeError. Vóór 3.4 was de standaard MD5, vandaar dat de Python-beheerders de standaard hebben verwijderd — zodat je een expliciete, veilige keuze moet maken.HMAC-SHA256 Base64, SHA-1, SHA-512 en MD5
De hmac.new() functie werkt met elk hash-algoritme dat beschikbaar is in hashlib. De meeste webhookproviders en API-gateways gebruiken HMAC-SHA256, maar je komt SHA-1 tegen in OAuth 1.0a, SHA-512 in protocollen die dat vereisen, en MD5 in verouderde systemen die nog niet zijn bijgewerkt.
HMAC-SHA256 met Base64-uitvoer
Veel webhookproviders sturen de handtekening als een Base64-gecodeerde string in een HTTP-header. Om hetzelfde formaat te produceren, geef je de ruwe .digest() bytes door aan base64.b64encode().
import hmac
import hashlib
import base64
key = b"whsec_MbkP7x9yFqHGn3tRdWz5"
payload = b'{"id":"evt_1Nq","type":"charge.succeeded","data":{"amount":4200}}'
# Ruwe digest → Base64 (gangbaar voor Authorization-headers en webhook-handtekeningen)
raw_digest = hmac.new(key, payload, hashlib.sha256).digest()
b64_signature = base64.b64encode(raw_digest).decode("ascii")
print(b64_signature)
# "dGhpcyBpcyBhIHNhbXBsZSBzaWduYXR1cmU="
# Dit is de waarde die je vergelijkt met de X-Signature-header
header_value = f"sha256={b64_signature}"
print(header_value)
# "sha256=dGhpcyBpcyBhIHNhbXBsZSBzaWduYXR1cmU="HMAC-SHA1 — Achterwaartse compatibiliteit met verouderde protocollen
SHA-1 wordt als zwak beschouwd voor nieuwe ontwerpen, maar HMAC-SHA1 is nog steeds vereist door OAuth 1.0a en sommige oudere webhook-implementaties. De code is identiek — wissel gewoon het algoritme uit.
import hmac
import hashlib
consumer_secret = b"oauth_consumer_secret_2026"
token_secret = b"oauth_token_secret_2026"
# OAuth 1.0a gebruikt consumer_secret&token_secret als ondertekeningssleutel
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..." — URL-codeer dit voor de Authorization-headerHMAC-SHA512 — Langere uitvoer
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 bytes (512 bits)
print(len(h.hexdigest())) # 128 hex characters
print(h.hexdigest()[:40] + "...")
# "8e3a1f9b2c4d6e7f0a1b3c5d7e9f0a2b4c6d8e0f..."HMAC-MD5 — Alleen voor verouderde systemen
import hmac import hashlib # MD5 is cryptografisch gebroken — gebruik alleen voor achterwaartse compatibiliteit key = b"legacy_api_key" msg = b"action=charge&amount=1500&merchant=store_42" sig = hmac.new(key, msg, hashlib.md5).hexdigest() print(sig) # 32-character hex string # "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
hmac.new() Parameters Overzicht
De constructor-handtekening is hmac.new(key, msg=None, digestmod). Alle drie functies in de module delen hetzelfde patroon voor sleutel- en algoritmeargumenten.
hmac.new() constructor
hmac.digest() eenmalig (Python 3.7+)
De parameter digestmod accepteert een callable (zoals hashlib.sha256) of een stringnaam (zoals "sha256"). De callable-vorm heeft de voorkeur omdat die wordt gevalideerd bij importtijd — een typefout in de stringvorm faalt pas bij runtime.
hmac.digest() — Snelle eenmalige HMAC (Python 3.7+)
Python 3.7 voegde hmac.digest(key, msg, digest) toe als een functie op moduleniveau. Het berekent de HMAC in één aanroep zonder een tussenliggend HMAC-object aan te maken. De returnwaarde zijn ruwe bytes (gelijkwaardig aan het aanroepen van .digest() op het object). Deze functie gebruikt een geoptimaliseerde C-implementatie op CPython en vermijdt de overhead van objectaanmaak, waardoor het meetbaar sneller is in tijdkritische lussen.
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}',
]
# Eenmalige digest — geen tussenliggend HMAC-object
signatures = [hmac.digest(key, msg, hashlib.sha256) for msg in messages]
# Converteer naar hex voor weergave
for msg, sig in zip(messages, signatures):
print(f"{msg[:30]}... -> {sig.hex()[:24]}...")De beperking: hmac.digest() geeft alleen ruwe bytes terug. Als je de hex-string direct nodig hebt, heb je nog steeds hmac.new() nodig voor de .hexdigest() methode, of je kunt .hex() ketenen op het bytes-resultaat.
hmac.digest() ondersteunt geen incrementele .update()-aanroepen. Als je een groot bestand in stukken leest en de inhoud wilt HMAC-en, gebruik dan hmac.new() en roep .update(chunk) aan in een lus.HMAC-handtekening verifiëren van webhook en API-respons
De meest voorkomende toepassing van HMAC in Python is het verifiëren van webhook-handtekeningen. Elke grote provider (Stripe, GitHub, Shopify, Twilio) ondertekent payloads met HMAC-SHA256 en stuurt de handtekening in een header. Het patroon is altijd hetzelfde: herbereken de HMAC over de ruwe verzoekbody en vergelijk daarna met hmac.compare_digest().
Webhook-handtekeningverificatie
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():
# Haal de ruwe body op — moet exact overeenkomen met wat is ondertekend
raw_body = request.get_data()
# Haal de handtekening op uit de header
received_sig = request.headers.get("X-Signature-256", "")
# Herbereken de HMAC over de ruwe body
expected_sig = hmac.new(WEBHOOK_SECRET, raw_body, hashlib.sha256).hexdigest()
# Constante-tijdvergelijking — voorkomt timing-aanvallen
if not hmac.compare_digest(f"sha256={expected_sig}", received_sig):
abort(403, "Invalid signature")
# Handtekening geverifieerd — verwerk het event
event = request.get_json()
print(f"Verified event: {event['type']} for {event['data']['amount']}")
return "", 200De functie hmac.compare_digest() vergelijkt twee strings of bytereeksen in constante tijd. Een gewone == vergelijking stopt bij de eerste niet-overeenkomende byte. Een aanvaller kan de responstijd meten over vele verzoeken en de verwachte handtekening geleidelijk byte voor byte reconstrueren. Constante-tijdvergelijking elimineert dit zijkanaal.
GitHub Webhook-verificatie
GitHub's webhookformaat illustreert het volledige patroon. Het stuurt een X-Hub-Signature-256 header met daarin sha256= gevolgd door de hex-gecodeerde HMAC-SHA256 van de ruwe verzoekbody, ondertekend met het webhookgeheim dat je configureert in je repository-instellingen. Het belangrijkste verschil met generieke webhookverificatie is dat je het sha256= voorvoegsel moet verwijderen vóór de vergelijking, en dat je de ruwe bytes van de verzoekbody moet lezen — eerst JSON parsen verandert de byterepresentatie en breekt de verificatie.
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 stuurt: 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() # ruwe bytes — parseer JSON pas hierna
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 "", 200Hetzelfde patroon geldt voor Shopify (X-Shopify-Hmac-SHA256) en Twilio (X-Twilio-Signature), met als enige verschil de headernaam en of de handtekening hex- of Base64-gecodeerd is. Controleer altijd de documentatie van de provider om het coderingsformaat te bevestigen — het door elkaar halen van hex en Base64 is de meest voorkomende oorzaak van handtekeningmismatchfouten.
API-verzoekverificatie met HMAC
Sommige API's vereisen dat de client elk verzoek ondertekent met HMAC. De ondertekeningsstring bevat doorgaans de HTTP-methode, het pad, de tijdstempel en de verzoekbody. Hieronder een patroon voor interne service-naar-service-verificatie.
import hmac
import hashlib
import time
import json
def sign_request(secret: bytes, method: str, path: str, body: str) -> dict:
"""Genereer een HMAC-SHA256-handtekening voor een API-verzoek."""
timestamp = str(int(time.time()))
# Bouw de ondertekeningsstring — methode + pad + tijdstempel + body
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,
}
# Gebruik
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..."}HTTP-verzoeken ondertekenen met de requests-bibliotheek
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=(",", ":")) # compacte JSON, deterministisch
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
# Stuur een ondertekend POST-verzoek
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())Korte noot: gebruik separators=(",", ":") bij het serialiseren van de body voor ondertekening. De standaard json.dumps() voegt spaties toe na scheidingstekens, waardoor de byterepresentatie verandert en handtekeningverificatie mislukt als de server anders serialiseert. Compacte JSON geeft je een canonieke vorm.
HMAC genereren via de opdrachtregel
Soms moet je een HMAC berekenen zonder een script te schrijven. Python's -c vlag en openssl doen dit allebei vanuit de terminal.
python3 -c " import hmac, hashlib print(hmac.new(b'my_secret', b'message_to_sign', hashlib.sha256).hexdigest()) " # uitvoer: 64-tekens hex-string
echo -n "message_to_sign" | openssl dgst -sha256 -hmac "my_secret" # SHA2-256(stdin)= 7d11... # Stuur een bestand door openssl HMAC openssl dgst -sha256 -hmac "my_secret" < payload.json
# Sla sleutel op in omgevingsvariabele om blootstelling via shell-geschiedenis te vermijden
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())
"echo -n is cruciaal — zonder die vlag voegt echo een newline-teken toe aan het bericht, waardoor de HMAC-uitvoer verandert. Dit is de meest voorkomende oorzaak van handtekeningmismatches bij het debuggen vanuit de terminal.Alternatief voor hoge prestaties — cryptography-bibliotheek
Voor de meeste toepassingen is de standaard hmac module snel genoeg. Als je de cryptography bibliotheek al gebruikt voor TLS of certificaatverwerking, biedt die ook HMAC via OpenSSL. Het belangrijkste praktische verschil met de stdlib is de op uitzonderingen gebaseerde .verify() API hieronder — die gooit bij een mismatch in plaats van een boolean terug te geven die je mogelijk vergeet te controleren.
pip install 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() # ruwe bytes
print(signature.hex())
# "9c4e2a..."
# Verificatiemodus — gooit InvalidSignature bij mismatch
h_verify = crypto_hmac.HMAC(key, hashes.SHA256())
h_verify.update(message)
h_verify.verify(signature) # gooit cryptography.exceptions.InvalidSignature indien onjuistDe .verify() methode van de cryptography bibliotheek is bijzonder handig: hij gooit een uitzondering bij mismatch in plaats van een boolean terug te geven. Dit maakt het moeilijker om een verificatiefout per ongeluk te negeren. De hmac.compare_digest() van de standaardbibliotheek geeft True/ False terug, wat stil kan worden genegeerd als de ontwikkelaar vergeet de returnwaarde te controleren.
Terminaluitvoer met syntaxismarkering
Als je HMAC-handtekeningen debugt in de terminal en gekleurde uitvoer wilt, rich werkt hiervoor goed.
pip install 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 (first 32 chars)", 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)Werken met grote bestanden — Incrementele HMAC
Voor bestanden groter dan 50 MB is het verspilling om alles in het geheugen te laden om een HMAC te berekenen. De .update() methode op het HMAC-object laat je data in stukken aanleveren. Dit houdt het geheugengebruik constant ongeacht de bestandsgrootte.
import hmac
import hashlib
def hmac_file(key: bytes, filepath: str, chunk_size: int = 8192) -> str:
"""Bereken HMAC-SHA256 van een bestand zonder het volledig in het geheugen te laden."""
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()
# Onderteken een databaseexport van 2 GB
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}")import hmac
import hashlib
def verify_file_hmac(key: bytes, filepath: str, expected_hex: str) -> bool:
"""Verifieer de HMAC-SHA256-handtekening van een bestand."""
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)
# Verifieer een gedownload artefact
is_valid = verify_file_hmac(
key=b"release_signing_key",
filepath="/tmp/release-v3.2.0.tar.gz",
expected_hex="8e3a1f9b2c4d6e7f0a1b3c5d7e9f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f",
)
print(f"Bestandsintegriteit: {'geldig' if is_valid else 'BESCHADIGD'}").update()-aanpak gebruikt een vaste chunk_size aan geheugen ongeacht de bestandsgrootte. Gebruik standaard 64 KB-stukken — groot genoeg om syscall-overhead te amortiseren, klein genoeg om binnen de L2-cache te blijven op de meeste hardware.Een cryptografisch veilige HMAC-sleutel genereren in Python
Een zwakke of voorspelbare sleutel ondermijnt de gehele HMAC-constructie. De secrets module (Python 3.6+) biedt cryptografisch sterke willekeurige bytes. Voor HMAC-SHA256, gebruik een 32-byte sleutel. Voor HMAC-SHA512, gebruik 64 bytes. Deze komen overeen met de interne blokgrootte van de respectieve hash-algoritmen, wat de optimale sleutellengte is conform RFC 2104.
import secrets
# Genereer sleutels die overeenkomen met de blokgrootte van het hash-algoritme
sha256_key = secrets.token_bytes(32) # 256 bits — voor HMAC-SHA256
sha512_key = secrets.token_bytes(64) # 512 bits — voor HMAC-SHA512
# Hex-weergave — veilig voor configuratiebestanden en omgevingsvariabelen
print(f"HMAC-SHA256 key: {sha256_key.hex()}")
# bijv. "a3f1b9c04e7d2f8a1b3c5d7e9f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f"
print(f"HMAC-SHA512 key: {sha512_key.hex()}")
# 128-tekens hex-string
# URL-veilige Base64 — compact, veilig voor HTTP-headers
import base64
b64_key = base64.urlsafe_b64encode(sha256_key).decode("ascii")
print(f"Base64 key: {b64_key}")
# bijv. "o_G5wE59L4obPF1-nwortG2ODwobPExdXnqLnA0dLi8="random.random() of random.randbytes() uit de random-module voor HMAC-sleutels. De standaard random-module gebruikt de Mersenne Twister PRNG, die voorspelbaar is na het observeren van 624 uitvoerwaarden. Gebruik altijd secrets.token_bytes() voor beveiligingsgevoelige willekeurigheid.Sleutellengte en RFC 2104-vereisten
RFC 2104 bepaalt dat de HMAC-sleutel elke lengte kan hebben, maar beveelt aan minimaal L bytes te gebruiken — waarbij L de uitvoerlengte is van de onderliggende hashfunctie. Voor HMAC-SHA256 zijn dat 32 bytes (256 bits). Sleutels korter dan L bits verminderen de veiligheidsmarge evenredig. Sleutels langer dan de blokgrootte van de hash (64 bytes voor SHA-256, 128 bytes voor SHA-512) worden eerst teruggezet naar de blokgrootte via hashing, dus sleutels langer dan de blokgrootte bieden geen extra voordeel. Gebruik 32 bytes voor HMAC-SHA256 en 64 bytes voor HMAC-SHA512.
Veilige sleutelopslag en -rotatie
Codeer HMAC-sleutels nooit hardcoded in broncode. De standaardaanpak voor productie-implementaties is de sleutel laden vanuit een omgevingsvariabele bij het opstarten: os.environ["HMAC_SECRET"].encode(). Voor omgevingen met hogere zekerheid bewaar je sleutels in een secrets management-systeem zoals AWS Secrets Manager, HashiCorp Vault of GCP Secret Manager en haal je ze op bij runtime. Deze systemen bieden auditlogboeken, toegangscontroles en automatische rotatie zonder dat een code-implementatie vereist is.
Plan sleutelrotatie vanaf het begin. Wanneer een sleutel wordt geroteerd, is er een venster waarin verzoeken die onderweg zijn ondertekend met de oude sleutel, zullen falen bij verificatie tegen de nieuwe sleutel. De standaardoplossing is een korte overlapsperiode: accepteer handtekeningen van zowel de oude als de nieuwe sleutel gedurende een korte tijd (minuten tot uren), en deactiveer daarna de oude sleutel. Als een sleutel is gecompromitteerd — blootgesteld in logs, gelekt via een git-commit of bekendgemaakt bij een incident — roteer dan onmiddellijk en beschouw alle handtekeningen geproduceerd met de gecompromitteerde sleutel als onbetrouwbaar. Herverifieer eventuele gecachte verificatieresultaten en informeer stroomafwaartse verbruikers over de sleutelwijziging.
bytearray en memoryview gebruiken met hmac.new()
De functie hmac.new() accepteert elk bytes-achtig object voor zowel de key- als msg-parameters. Dit betekent dat je bytes, bytearray of memoryview direct kunt doorgeven, zonder kopiëren of converteren. Dit is het meest relevant in twee scenario's: netwerk-protocol-implementaties waarbij socket.recv_into() data schrijft naar een vooraf toegewezen bytearray buffer, en systemen met hoge doorvoer waarbij het vermijden van tussenliggende kopieën de garbage collection-druk vermindert. Een memoryview slice is zero-copy: het biedt een venster in de originele buffer zonder nieuw geheugen toe te wijzen. Bij tienduizenden berichten per seconde maakt het elimineren van die toewijzingen een meetbaar verschil in latentie en doorvoer.
import hmac
import hashlib
# bytearray — veranderlijke bytes, handig voor binaire protocol-buffers
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"Frame signature: {sig[:32]}...")
# memoryview — zero-copy slice van een grotere buffer
large_buffer = bytearray(4096)
large_buffer[:20] = b"sensor_reading_12345"
# HMAC alleen de eerste 20 bytes zonder te kopiëren
view = memoryview(large_buffer)[:20]
sig = hmac.new(key, view, hashlib.sha256).hexdigest()
print(f"Sensor signature: {sig[:32]}...")Veelgemaakte fouten
De eerste twee fouten zie ik in bijna elke code review met webhook-handlers. Ze zijn makkelijk te introduceren onder tijdsdruk en moeilijk te herkennen zonder te weten waar je op moet letten.
Probleem: De ==-operator stopt bij de eerste byte-mismatch, waardoor tijdinformatie lekt die een aanvaller in staat stelt de verwachte handtekening incrementeel te reconstrueren.
Oplossing: Gebruik altijd hmac.compare_digest() voor handtekeningvergelijking — het draait in constante tijd ongeacht waar de mismatch optreedt.
received_sig = request.headers["X-Signature"]
expected_sig = hmac.new(key, body, hashlib.sha256).hexdigest()
if received_sig == expected_sig: # KWETSBAAR voor timing-aanval
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): # constante tijd
process_webhook(body)Probleem: hmac.new() vereist bytes-achtige objecten. Een Python str doorgeven geeft TypeError: "key: expected bytes or bytearray, but got 'str'".
Oplossing: Roep .encode() aan op string-sleutels en -berichten voordat je ze doorgeeft aan hmac.new().
key = "my_api_secret" # str, niet bytes
msg = '{"event":"test"}' # str, niet 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)Probleem: hmac.new(key, msg) aanroepen zonder het derde argument geeft TypeError. Vóór Python 3.4 was de standaard MD5, maar die is verwijderd om veiligheidsredenen.
Oplossing: Geef altijd het algoritme expliciet mee: hashlib.sha256, hashlib.sha512, of wat je protocol vereist.
# Ontbrekende digestmod — geeft TypeError in Python 3.4+ sig = hmac.new(key, msg).hexdigest()
# Geef altijd het hash-algoritme op sig = hmac.new(key, msg, hashlib.sha256).hexdigest()
Probleem: Veel webhookproviders (Stripe, Shopify) sturen Base64-gecodeerde handtekeningen, niet hex. Een hex-string vergelijken met een Base64-waarde mislukt altijd, waardoor alle webhooks worden geweigerd.
Oplossing: Controleer de documentatie van de provider voor het handtekeningformaat. Als ze Base64 gebruiken, codeer de ruwe .digest()-bytes, niet de .hexdigest()-string.
# Provider stuurt Base64, maar wij berekenen hex — komt nooit overeen expected = hmac.new(key, body, hashlib.sha256).hexdigest() # "a3f1b9c0..." vs "o/G5wE59..." — altijd mismatch
import base64
# Gebruik het formaat van de provider: ruwe bytes → Base64
raw = hmac.new(key, body, hashlib.sha256).digest()
expected = base64.b64encode(raw).decode("ascii")
# "o/G5wE59..." — komt overeen met de headerstdlib hmac vs cryptography — Snel overzicht
Gebruik de stdlib hmac module voor webhook-verificatie, API-ondertekening en algemene HMAC-bewerkingen — geen afhankelijkheden en dekt elk standaardalgoritme. Gebruik hmac.digest() voor batchbewerkingen waarbij de eenmalige snelheid telt. Grijp naar de cryptography bibliotheek alleen wanneer je er al van afhankelijk bent voor andere bewerkingen (TLS, X.509, symmetrische versleuteling) en de op uitzonderingen gebaseerde .verify() API wilt. Voor snelle handtekeningcontroles zonder Python te schrijven, gebruik de HMAC Generator tool om je sleutel en bericht te plakken en het resultaat direct te ontvangen.
Veelgestelde vragen
Wat is het verschil tussen hmac.new() en hmac.digest() in Python?
hmac.new() geeft een HMAC-object terug dat incrementele .update()-aanroepen ondersteunt en zowel .digest() (ruwe bytes) als .hexdigest() (hex string) biedt. hmac.digest() is een eenmalige functie toegevoegd in Python 3.7 die direct ruwe bytes teruggeeft zonder een tussenliggend object aan te maken. Gebruik hmac.digest() wanneer je het volledige bericht al hebt en alleen het resultaat nodig hebt. Gebruik hmac.new() wanneer je data in stukken wilt aanleveren of de hex-uitvoer nodig hebt.
import hmac, hashlib
key = b"webhook_secret_2026"
body = b'{"event":"payment.completed","amount":9950}'
# Eenmalig — geeft ruwe bytes terug
raw = hmac.digest(key, body, hashlib.sha256)
# Op objectbasis — ondersteunt incrementele updates en hex-uitvoer
h = hmac.new(key, body, hashlib.sha256)
hex_sig = h.hexdigest()Hoe verifieer ik een HMAC-handtekening in Python?
Bereken de HMAC opnieuw over het originele bericht met de gedeelde sleutel, en vergelijk daarna met hmac.compare_digest(). Gebruik nooit == voor handtekeningvergelijking. De ==-operator is kwetsbaar voor timing-aanvallen omdat hij stopt bij de eerste niet-overeenkomende byte, waardoor informatie over de verwachte handtekeninglengte en -inhoud lekt.
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)Is Python HMAC-SHA256 hetzelfde als hashing met hashlib.sha256?
Nee. hashlib.sha256 berekent een gewone SHA-256-hash van de invoer, die iedereen kan reproduceren. HMAC-SHA256 mengt een geheime sleutel in de hashberekening conform RFC 2104, zodat alleen iemand met de sleutel de juiste uitvoer kan produceren of verifiëren. Een gewone hash bewijst data-integriteit. Een HMAC bewijst zowel integriteit als authenticiteit.
import hmac, hashlib msg = b"transfer:9950:USD" key = b"api_secret_k8x2" plain_hash = hashlib.sha256(msg).hexdigest() # iedereen kan dit berekenen hmac_hash = hmac.new(key, msg, hashlib.sha256).hexdigest() # vereist de sleutel
Kan ik HMAC-SHA1 gebruiken in Python 3?
Ja, geef hashlib.sha1 mee als digestmod-argument. HMAC-SHA1 werkt prima in Python 3 en de hmac-module heeft er geen deprecatiewaarschuwingen voor. SHA-1 wordt echter als zwak beschouwd voor nieuwe ontwerpen — de botsingsbestendigheid ligt onder de 80 bits en NIST heeft het in 2015 afgeraden voor de meeste toepassingen met digitale handtekeningen. De belangrijkste reden om HMAC-SHA1 vandaag te gebruiken is achterwaartse compatibiliteit met bestaande protocollen die het vereisen, zoals OAuth 1.0a of bepaalde verouderde webhooksystemen. Als je beide zijden van de integratie beheert, gebruik dan HMAC-SHA256 of HMAC-SHA512 voor al het nieuwe werk.
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()
Hoe genereer ik een veilige HMAC-sleutel in Python?
Gebruik secrets.token_bytes() uit de standaardbibliotheek. Voor HMAC-SHA256 is een 32-byte sleutel de standaardaanbeveling, omdat die overeenkomt met de hash-blokgrootte. Voor HMAC-SHA512 gebruik je 64 bytes. Gebruik geen random.random() of os.urandom() voor sleutelgeneratie in applicatiecode — secrets is de juiste module voor beveiligingsgevoelige willekeurigheid sinds Python 3.6.
import secrets hmac_sha256_key = secrets.token_bytes(32) # 256 bits hmac_sha512_key = secrets.token_bytes(64) # 512 bits # Opslaan als hex voor configuratiebestanden print(hmac_sha256_key.hex()) # bijv. "a3f1b9c04e..."
Waarom vereist hmac.new() digestmod in Python 3?
Vóór Python 3.4 was de standaardwaarde van digestmod MD5, dat cryptografisch gebroken is — MD5 heeft bekende botsingsaanvallen en mag nooit worden gebruikt in nieuwe beveiligingsgevoelige code. De Python-beheerders hebben de standaard verwijderd om een expliciete algoritmekkeuze af te dwingen, zodat ontwikkelaars niet onbewust MD5-gebaseerde MACs implementeren. Als je hmac.new(key, msg) aanroept zonder digestmod, krijg je een TypeError. Geef altijd het algoritme expliciet mee: hashlib.sha256, hashlib.sha512, of een andere hashlib-constructor. Als je twijfelt is hashlib.sha256 de veilige standaard — geen bekende zwakheden en snel genoeg voor elke praktische toepassing.
import hmac, hashlib key = b"secret" msg = b"data" # Dit geeft TypeError in Python 3.4+ # hmac.new(key, msg) # TypeError: missing required argument: 'digestmod' # Geef altijd het algoritme op h = hmac.new(key, msg, hashlib.sha256)
Voor een snelle HMAC-controle zonder een Python-script op te starten, plak je sleutel en bericht in de online HMAC Generator — ondersteunt SHA-256, SHA-384 en SHA-512 met directe resultaten.
Gerelateerde tools
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.
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.