HMAC Python — hmac.new() SHA-256 Gids + Code-voorbeelden

·DevOps Engineer & Python Automation Specialist·Beoordeeld doorMaria Santos·Gepubliceerd

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.

Before · Python
After · Python
# 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".

Python 3.7+ — minimaal HMAC-SHA256-voorbeeld
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.

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

# 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.

Python 3.7+ — strings naar bytes coderen
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-uitvoer
Opmerking:De parameter digestmod 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().

Python 3.7+ — HMAC-SHA256 Base64-codering
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.

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 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-header

HMAC-SHA512 — Langere uitvoer

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 bytes (512 bits)
print(len(h.hexdigest()))  # 128 hex characters
print(h.hexdigest()[:40] + "...")
# "8e3a1f9b2c4d6e7f0a1b3c5d7e9f0a2b4c6d8e0f..."

HMAC-MD5 — Alleen voor verouderde systemen

Python 3.7+ — HMAC-MD5
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"
Waarschuwing:HMAC-MD5 is alleen acceptabel voor achterwaartse compatibiliteit met systemen die je niet kunt migreren. Gebruik voor elk nieuw project minimaal HMAC-SHA256. MD5 heeft bekende kwetsbaarheden voor botsingsaanvallen die, hoewel minder direct uitbuitbaar in HMAC-modus, het een slechte standaardkeuze maken.

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

Parameter
Type
Standaard
Beschrijving
key
bytes | bytearray
(verplicht)
De geheime sleutel — moet bytes zijn, niet een string
msg
bytes | None
None
Eerste bericht om te hashen; meer data kan later worden toegevoegd via .update()
digestmod
str | callable
(verplicht)
Hash-algoritme — bijv. hashlib.sha256 of de string "sha256"

hmac.digest() eenmalig (Python 3.7+)

Parameter
Type
Beschrijving
key
bytes
De geheime sleutel
msg
bytes
Het te authenticeren bericht
digest
str | callable
Hash-algoritme — hetzelfde als digestmod in hmac.new()

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.

Python 3.7+ — hmac.digest() eenmalig
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.

Opmerking: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

Python 3.7+ — webhook HMAC-verificatie (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():
    # 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 "", 200

De 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.

Python 3.7+ — GitHub X-Hub-Signature-256-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 "", 200

Hetzelfde 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.

Python 3.7+ — API-verzoeken ondertekenen met HMAC-SHA256
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

Python 3.7+ — HMAC-ondertekende verzoeken 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.

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())
"
# uitvoer: 64-tekens hex-string
bash — HMAC-SHA256 via openssl (ter vergelijking)
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
bash — HMAC vanuit omgevingsvariabele sleutel
# 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())
"
Opmerking:De vlag 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.

bash — cryptography installeren
pip install cryptography
Python 3.7+ — HMAC via de cryptography-bibliotheek
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 onjuist

De .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.

bash — rich installeren
pip install rich
Python 3.7+ — gekleurde HMAC-uitvoer met 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)
Opmerking:Rich is alleen bedoeld voor terminalweergave. Gebruik het niet bij het schrijven van HMAC-handtekeningen naar bestanden, HTTP-headers of logaggregatie-systemen — de ANSI-escapecodes beschadigen de uitvoer.

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.

Python 3.7+ — HMAC van een groot bestand in stukken
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}")
Python 3.7+ — HMAC-handtekening van een gedownload bestand verifiëren
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'}")
Opmerking:Schakel over naar incrementele HMAC wanneer het bestand groter is dan 50-100 MB of bij het verwerken van uploads in een webserver waar geheugen per verzoek telt. De .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.

Python 3.7+ — HMAC-sleutels genereren met secrets
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="
Waarschuwing:Gebruik nooit 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.

Python 3.7+ — bytearray en memoryview met HMAC
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.

HMAC-handtekeningen vergelijken met == in plaats van hmac.compare_digest()

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.

Before · Python
After · Python
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)
Een string in plaats van bytes doorgeven aan hmac.new()

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().

Before · Python
After · Python
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)
digestmod vergeten (Python 3.4+)

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.

Before · Python
After · Python
# 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()
.hexdigest() gebruiken terwijl de provider Base64 verwacht

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.

Before · Python
After · Python
# 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 header

stdlib hmac vs cryptography — Snel overzicht

Methode
Algoritme
Uitvoer
Streaming
Aangepaste typen
Installatie vereist
hmac.new() + hexdigest()
Elk hashlib
Hex string
✓ via .update()
N.v.t.
Nee (stdlib)
hmac.new() + digest()
Elk hashlib
Ruwe bytes
✓ via .update()
N.v.t.
Nee (stdlib)
hmac.digest()
Elk hashlib
Ruwe bytes
✗ (eenmalig)
N.v.t.
Nee (stdlib, 3.7+)
hashlib.sha256 (gewone hash)
Alleen SHA-256
Hex of bytes
✓ via .update()
N.v.t.
Nee (stdlib)
cryptography (HMAC)
Elk
Ruwe bytes
✓ via .update()
N.v.t.
pip install
pyca/cryptography + CMAC
AES-CMAC
Ruwe bytes
✓ via .update()
N.v.t.
pip install

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.

Python
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.

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)

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.

Python
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.

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()

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.

Python
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.

Python
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

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 SantosTechnisch beoordelaar

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.