HMAC Python — hmac.new() SHA-256 Anleitung mit Beispielen
Nutze das kostenlose HMAC Generator direkt im Browser – keine Installation erforderlich.
HMAC Generator online testen →Jeder Webhook-Callback, jede signierte API-Anfrage, jede Stripe- oder GitHub-Ereignisbenachrichtigung verwendet eine HMAC-Signatur, um zu beweisen, dass die Payload nicht manipuliert wurde. Pythons hmac Modul verarbeitet HMAC-SHA256 in Python mit einem einzigen Funktionsaufruf: hmac.new(key, msg, hashlib.sha256). Kein pip install, keine C-Erweiterung, keine Drittanbieter-Abhängigkeit. Für schnelle einmalige Signaturprüfungen ohne Code zu schreiben, liefert der Online-HMAC-Generator das Ergebnis sofort. Diese Anleitung behandelt hmac.new(), hmac.digest(), hmac.compare_digest(), Base64-Kodierung, Webhook-Verifizierung, API-Request-Signierung und alle Hash-Algorithmen von SHA-1 bis SHA-512. Alle Beispiele richten sich an Python 3.7+.
- ✓hmac.new(key, msg, hashlib.sha256) ist der Standard-Einstiegspunkt — key und msg müssen bytes sein, digestmod ist seit Python 3.4 verpflichtend.
- ✓hmac.digest(key, msg, "sha256") ist eine schnellere einmalige Alternative, die in Python 3.7 eingeführt wurde — gibt rohe Bytes zurück, kein Zwischenobjekt.
- ✓Signaturen immer mit hmac.compare_digest() verifizieren, um Timing-Angriffe zu verhindern — niemals == für HMAC-Vergleiche verwenden.
- ✓Rohe .digest()-Ausgabe für HTTP-Header und Webhook-Signaturen Base64-kodieren: base64.b64encode(h.digest()).
- ✓Das hmac-Modul akzeptiert jeden hashlib-Algorithmus: sha1, sha256, sha384, sha512, md5, blake2b.
Was ist HMAC?
HMAC (Hash-based Message Authentication Code) ist eine in RFC 2104 definierte Konstruktion, die einen geheimen Schlüssel mit einer Hash-Funktion kombiniert, um ein Authentifizierungstag fester Größe zu erzeugen. Im Gegensatz zu einem einfachen Hash (den jeder berechnen kann) erfordert ein HMAC die Kenntnis des geheimen Schlüssels. Damit lässt sich sowohl die Integrität als auch die Authentizität einer Nachricht prüfen. Ändert sich auch nur ein einziges Byte der Nachricht oder des Schlüssels, ist die Ausgabe vollständig anders. Die Konstruktion funktioniert, indem der Schlüssel XOR-verknüpft mit zwei verschiedenen Padding-Konstanten (ipad und opad) gehasht wird und die Nachricht zwischen zwei Hash-Operationen eingebettet wird. Pythons hmac Modul implementiert diesen RFC direkt.
# Einfacher SHA-256-Hash — kein geheimer Schlüssel, jeder kann ihn berechnen hashlib.sha256(b"payment:9950:USD").hexdigest() # "7a3b1c..." (deterministisch, öffentlich)
# HMAC-SHA256 — erfordert den geheimen Schlüssel zur Erzeugung hmac.new(b"api_secret", b"payment:9950:USD", hashlib.sha256).hexdigest() # "e4f2a8..." (nur der Schlüsselinhaber kann es berechnen)
hmac.new() — Der Standard-Einstiegspunkt
Das hmac Modul ist Teil der Python-Standardbibliothek. Zwei Imports und du bist startklar: import hmac, hashlib. Die drei wichtigsten Funktionen sind hmac.new() (erstellt ein HMAC-Objekt), hmac.digest() (einmalig, Python 3.7+) und hmac.compare_digest() (zeitkonstanter Vergleich). Kein pip install erforderlich.
hmac.new(key, msg, digestmod) nimmt drei Argumente. Sowohl key als auch msg müssen bytes-artige Objekte sein ( bytes, bytearray oder memoryview). Das digestmod Argument ist seit Python 3.4 verpflichtend und akzeptiert jeden hashlib Konstruktor (wie hashlib.sha256) oder einen String-Namen wie "sha256".
import hmac
import hashlib
key = b"webhook_signing_key_2026"
message = b'{"event":"invoice.paid","invoice_id":"inv_8f3a","amount":19900}'
# HMAC-Objekt erstellen und die Hex-Signatur abrufen
signature = hmac.new(key, message, hashlib.sha256).hexdigest()
print(signature)
# "b4e74f6c9a1d3e5f8b2a7c0d4e6f1a3b5c7d9e0f2a4b6c8d0e1f3a5b7c9d0e2f"Das HMAC-Objekt bietet zwei Ausgabemethoden. .digest() gibt rohe Bytes zurück (32 Bytes für SHA-256, 64 für SHA-512). .hexdigest() gibt einen Kleinbuchstaben-Hex-String zurück. Der Hex-String ist ein gewöhnlicher Python str — kein Dekodierungsschritt erforderlich.
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 # Sie repräsentieren dieselben Daten — Hex ist nur eine String-Kodierung der Bytes assert raw_bytes.hex() == hex_string
Wenn dein Schlüssel oder deine Nachricht ein Python-String ist, rufe .encode() auf, um ihn in Bytes umzuwandeln, bevor du ihn an hmac.new() übergibst. Das stolpert fast jeden beim ersten Mal — Python 3 Strings sind Unicode, keine Bytes, und das hmac-Modul lehnt sie ab.
import hmac
import hashlib
# String-Schlüssel und Nachricht — .encode() konvertiert zu 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..." — konsistente Hex-String-Ausgabedigestmod Parameter hat seit Python 3.4 keinen Standardwert mehr. Der Aufruf von hmac.new(key, msg) ohne ihn wirft TypeError. Vor 3.4 war MD5 der Standard, weshalb die Python-Maintainer den Standard entfernt haben — um eine explizite, sichere Wahl zu erzwingen.HMAC-SHA256 Base64, SHA-1, SHA-512 und MD5
Die hmac.new() Funktion arbeitet mit jedem Hash-Algorithmus, der in hashlib verfügbar ist. Die meisten Webhook-Anbieter und API-Gateways verwenden HMAC-SHA256, aber du wirst SHA-1 in OAuth 1.0a, SHA-512 in Protokollen, die es vorschreiben, und MD5 in Legacy-Systemen begegnen, die noch nicht aktualisiert wurden.
HMAC-SHA256 mit Base64-Ausgabe
Viele Webhook-Anbieter senden die Signatur als Base64-kodierten String in einem HTTP-Header. Um das gleiche Format zu erzeugen, übergib die rohen .digest() Bytes an base64.b64encode().
import hmac
import hashlib
import base64
key = b"whsec_MbkP7x9yFqHGn3tRdWz5"
payload = b'{"id":"evt_1Nq","type":"charge.succeeded","data":{"amount":4200}}'
# Roher Digest → Base64 (üblich für Authorization-Header und Webhook-Signaturen)
raw_digest = hmac.new(key, payload, hashlib.sha256).digest()
b64_signature = base64.b64encode(raw_digest).decode("ascii")
print(b64_signature)
# "dGhpcyBpcyBhIHNhbXBsZSBzaWduYXR1cmU="
# Dieser Wert wird mit dem X-Signature-Header verglichen
header_value = f"sha256={b64_signature}"
print(header_value)
# "sha256=dGhpcyBpcyBhIHNhbXBsZSBzaWduYXR1cmU="HMAC-SHA1 — Legacy-Protokoll-Kompatibilität
SHA-1 gilt für neue Entwürfe als schwach, aber HMAC-SHA1 wird von OAuth 1.0a und einigen älteren Webhook-Implementierungen noch vorgeschrieben. Der Code ist identisch — tausche einfach den Algorithmus aus.
import hmac
import hashlib
consumer_secret = b"oauth_consumer_secret_2026"
token_secret = b"oauth_token_secret_2026"
# OAuth 1.0a verwendet consumer_secret&token_secret als Signierschlüssel
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-kodiere dies für den Authorization-HeaderHMAC-SHA512 — Längere Ausgabe
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-Zeichen
print(h.hexdigest()[:40] + "...")
# "8e3a1f9b2c4d6e7f0a1b3c5d7e9f0a2b4c6d8e0f..."HMAC-MD5 — Nur für Legacy
import hmac import hashlib # MD5 ist kryptografisch gebrochen — nur für Legacy-Protokoll-Kompatibilität verwenden 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-stelliger Hex-String # "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
hmac.new() Parameter-Referenz
Die Konstruktorsignatur lautet hmac.new(key, msg=None, digestmod). Alle drei Funktionen im Modul folgen dem gleichen Muster für Schlüssel- und Algorithmus-Argumente.
hmac.new() Konstruktor
hmac.digest() einmalig (Python 3.7+)
Der digestmod Parameter akzeptiert entweder ein Callable (wie hashlib.sha256) oder einen String-Namen (wie "sha256"). Die Callable-Form wird bevorzugt, da sie zur Import-Zeit validiert wird — ein Tippfehler in der String-Form schlägt erst zur Laufzeit fehl.
hmac.digest() — Schnelles einmaliges HMAC (Python 3.7+)
Python 3.7 hat hmac.digest(key, msg, digest) als Funktion auf Modulebene hinzugefügt. Sie berechnet den HMAC in einem einzigen Aufruf ohne ein Zwischen-HMAC-Objekt zu erzeugen. Der Rückgabewert sind rohe Bytes (äquivalent zum Aufruf von .digest() auf dem Objekt). Diese Funktion verwendet eine optimierte C-Implementierung auf CPython und vermeidet den Objekt-Allokations-Overhead, was sie in engen Schleifen messbar schneller macht.
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}',
]
# Einmaliger Digest — kein Zwischen-HMAC-Objekt
signatures = [hmac.digest(key, msg, hashlib.sha256) for msg in messages]
# In Hex für die Ausgabe konvertieren
for msg, sig in zip(messages, signatures):
print(f"{msg[:30]}... -> {sig.hex()[:24]}...")Die Einschränkung: hmac.digest() gibt nur rohe Bytes zurück. Wenn du den Hex-String direkt benötigst, brauchst du weiterhin hmac.new() für seine .hexdigest() Methode oder verkette .hex() auf das Bytes-Ergebnis.
hmac.digest() unterstützt keine inkrementellen .update() Aufrufe. Wenn du eine große Datei in Blöcken liest und deren Inhalt mit HMAC verarbeiten musst, bleib bei hmac.new() und rufe .update(chunk) in einer Schleife auf.HMAC-Signatur von Webhooks und API-Antworten verifizieren
Die häufigste Verwendung von HMAC in Python ist die Verifizierung von Webhook-Signaturen. Jeder große Anbieter (Stripe, GitHub, Shopify, Twilio) signiert Payloads mit HMAC-SHA256 und sendet die Signatur in einem Header. Das Muster ist immer gleich: den HMAC über den rohen Request-Body neu berechnen, dann mit hmac.compare_digest() vergleichen.
Webhook-Signatur-Verifizierung
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():
# Rohen Body abrufen — muss exakt dem signierten Inhalt entsprechen
raw_body = request.get_data()
# Signatur aus dem Header lesen
received_sig = request.headers.get("X-Signature-256", "")
# HMAC über den rohen Body neu berechnen
expected_sig = hmac.new(WEBHOOK_SECRET, raw_body, hashlib.sha256).hexdigest()
# Zeitkonstanter Vergleich — verhindert Timing-Angriffe
if not hmac.compare_digest(f"sha256={expected_sig}", received_sig):
abort(403, "Ungültige Signatur")
# Signatur verifiziert — Ereignis verarbeiten
event = request.get_json()
print(f"Verifiziertes Ereignis: {event['type']} für {event['data']['amount']}")
return "", 200Die hmac.compare_digest() Funktion vergleicht zwei Strings oder Byte-Sequenzen in konstanter Zeit. Ein normaler == Vergleich schließt beim ersten nicht übereinstimmenden Byte kurz. Ein Angreifer kann die Antwortzeit über viele Anfragen messen und die erwartete Signatur Byte für Byte schrittweise rekonstruieren. Der zeitkonstante Vergleich eliminiert diesen Seitenkanal.
GitHub-Webhook-Verifizierung
GitHubs Webhook-Format veranschaulicht das vollständige Muster. Es sendet einen X-Hub-Signature-256 Header, der sha256= gefolgt vom Hex-kodierten HMAC-SHA256 des rohen Request-Bodys enthält, signiert mit dem Webhook-Geheimnis, das du in deinen Repository-Einstellungen konfigurierst. Der wesentliche Unterschied zur allgemeinen Webhook-Verifizierung ist, dass du das sha256= Präfix vor dem Vergleich entfernen musst und die rohen Bytes des Request-Bodys lesen musst — das vorherige Parsen des JSON ändert die Byte-Darstellung und bricht die Verifizierung.
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 sendet: X-Hub-Signature-256: sha256=<hex_digest>
sig_header = request.headers.get("X-Hub-Signature-256", "")
if not sig_header.startswith("sha256="):
abort(403, "Fehlender oder fehlerhafter Signatur-Header")
received_hex = sig_header[len("sha256="):]
raw_body = request.get_data() # rohe Bytes — JSON nicht vorher parsen
expected_hex = hmac.new(
GITHUB_WEBHOOK_SECRET, raw_body, hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected_hex, received_hex):
abort(403, "Signatur stimmt nicht überein — Payload wurde möglicherweise manipuliert")
event_type = request.headers.get("X-GitHub-Event", "unknown")
payload = request.get_json()
print(f"Verifiziertes GitHub {event_type}-Ereignis: {payload.get('action', '')}")
return "", 200Das gleiche Muster gilt für Shopify (X-Shopify-Hmac-SHA256) und Twilio (X-Twilio-Signature), mit dem einzigen Unterschied im Header-Namen und ob die Signatur Hex- oder Base64-kodiert ist. Prüfe immer die Anbieterdokumentation, um das Kodierungsformat zu bestätigen — das Verwechseln von Hex und Base64 ist die häufigste Ursache für Signatur-Fehlanpassungsfehler.
API-Request-Authentifizierung mit HMAC
Einige APIs verlangen, dass der Client jede Anfrage mit HMAC signiert. Der signierte String enthält normalerweise die HTTP-Methode, den Pfad, den Zeitstempel und den Request-Body. Hier ist ein Muster für die interne Dienst-zu-Dienst-Authentifizierung.
import hmac
import hashlib
import time
import json
def sign_request(secret: bytes, method: str, path: str, body: str) -> dict:
"""HMAC-SHA256-Signatur für eine API-Anfrage generieren."""
timestamp = str(int(time.time()))
# Signier-String aufbauen — Methode + Pfad + Zeitstempel + 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,
}
# Verwendung
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-Anfragen mit der requests-Bibliothek signieren
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=(",", ":")) # kompaktes 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"Signierte Anfrage fehlgeschlagen: {e}") from e
# Signierte POST-Anfrage senden
resp = make_signed_request("POST", "/api/v2/invoices", {
"customer_id": "cust_4421",
"line_items": [
{"description": "Pro plan - März 2026", "amount": 4900},
{"description": "Zusätzliche Plätze (3)", "amount": 2100},
],
})
print(resp.status_code, resp.json())Wichtiger Hinweis: Verwende separators=(",", ":") beim Serialisieren des Bodys zum Signieren. Das Standard- json.dumps() fügt nach Trennzeichen Leerzeichen ein, was die Byte-Darstellung verändert und die Signatur- Verifizierung bricht, wenn der Server anders serialisiert. Kompaktes JSON liefert eine kanonische Form.
HMAC-Generierung über die Kommandozeile
Manchmal muss ein HMAC berechnet werden, ohne ein Skript zu schreiben. Pythons -c Flag und openssl erledigen das direkt im Terminal.
python3 -c " import hmac, hashlib print(hmac.new(b'my_secret', b'message_to_sign', hashlib.sha256).hexdigest()) " # Ausgabe: 64-stelliger Hex-String
echo -n "message_to_sign" | openssl dgst -sha256 -hmac "my_secret" # SHA2-256(stdin)= 7d11... # Datei durch openssl HMAC leiten openssl dgst -sha256 -hmac "my_secret" < payload.json
# Schlüssel in Umgebungsvariable speichern, um Shell-Verlauf-Exposition zu vermeiden
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 Flag ist entscheidend — ohne es fügt echo ein Zeilenumbruchzeichen an die Nachricht an, was die HMAC-Ausgabe verändert. Das ist die häufigste Ursache für Signatur-Fehlanpassungen beim Debuggen im Terminal.Hochleistungsalternative — cryptography-Bibliothek
Für die meisten Anwendungen ist das Standard- hmac Modul schnell genug. Wenn du die cryptography Bibliothek bereits für TLS oder Zertifikatsverarbeitung verwendest, bietet sie ebenfalls HMAC auf Basis von OpenSSL. Der wesentliche praktische Unterschied zur Standardbibliothek ist die ausnahmebasierte .verify() API — sie wirft bei Nichtübereinstimmung eine Ausnahme, anstatt einen booleschen Wert zurückzugeben, den man versehentlich ignorieren könnte.
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() # rohe Bytes
print(signature.hex())
# "9c4e2a..."
# Verifizierungsmodus — wirft InvalidSignature bei Nichtübereinstimmung
h_verify = crypto_hmac.HMAC(key, hashes.SHA256())
h_verify.update(message)
h_verify.verify(signature) # wirft cryptography.exceptions.InvalidSignature wenn falschDie .verify() Methode der cryptography Bibliothek ist besonders praktisch: Sie wirft bei Nichtübereinstimmung eine Ausnahme statt einen booleschen Wert zurückzugeben. Das macht es schwieriger, einen Verifizierungsfehler versehentlich zu ignorieren. Die hmac.compare_digest() Funktion der Standardbibliothek gibt True/ False zurück, was stillschweigend ignoriert werden kann, wenn der Entwickler vergisst, den Rückgabewert zu prüfen.
Terminal-Ausgabe mit Syntax-Hervorhebung
Wenn du HMAC-Signaturen im Terminal debuggst und farbige Ausgabe möchtest, rich erledigt das gut.
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 Signaturen")
table.add_column("Endpunkt", style="cyan")
table.add_column("Signatur (erste 32 Zeichen)", 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)Große Dateien verarbeiten — inkrementelles HMAC
Bei Dateien über etwa 50 MB ist es verschwenderisch, alles in den Speicher zu laden, nur um ein HMAC zu berechnen. Die .update() Methode des HMAC-Objekts erlaubt es, Daten in Blöcken zu verarbeiten. Damit bleibt der Speicherverbrauch unabhängig von der Dateigröße konstant.
import hmac
import hashlib
def hmac_file(key: bytes, filepath: str, chunk_size: int = 8192) -> str:
"""HMAC-SHA256 einer Datei berechnen, ohne sie vollständig in den Speicher zu 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"Datei '{filepath}' kann nicht gelesen werden: {e}") from e
return h.hexdigest()
# 2-GB-Datenbankexport signieren
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:
"""Die HMAC-SHA256-Signatur einer Datei verifizieren."""
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)
# Heruntergeladenes Artefakt verifizieren
is_valid = verify_file_hmac(
key=b"release_signing_key",
filepath="/tmp/release-v3.2.0.tar.gz",
expected_hex="8e3a1f9b2c4d6e7f0a1b3c5d7e9f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f",
)
print(f"Dateiintegrität: {'gültig' if is_valid else 'BESCHÄDIGT'}").update() Ansatz verwendet unabhängig von der Dateigröße einen festen chunk_size an Speicher. Als Standard werden 64 KB Blöcke empfohlen — groß genug, um Syscall-Overhead zu amortisieren, klein genug, um im L2-Cache der meisten Hardware zu bleiben.Kryptografisch sichere HMAC-Schlüssel in Python generieren
Ein schwacher oder vorhersehbarer Schlüssel untergräbt die gesamte HMAC-Konstruktion. Das secrets Modul (Python 3.6+) liefert kryptografisch starke Zufallsbytes. Für HMAC-SHA256 verwende einen 32-Byte-Schlüssel. Für HMAC-SHA512 verwende 64 Bytes. Diese entsprechen der internen Blockgröße der jeweiligen Hash-Algorithmen, was die optimale Schlüssellänge gemäß RFC 2104 ist.
import secrets
# Schlüssel passend zur Blockgröße des Hash-Algorithmus generieren
sha256_key = secrets.token_bytes(32) # 256 Bits — für HMAC-SHA256
sha512_key = secrets.token_bytes(64) # 512 Bits — für HMAC-SHA512
# Hex-Darstellung — sicher für Konfigurationsdateien und Umgebungsvariablen
print(f"HMAC-SHA256-Schlüssel: {sha256_key.hex()}")
# z. B. "a3f1b9c04e7d2f8a1b3c5d7e9f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f"
print(f"HMAC-SHA512-Schlüssel: {sha512_key.hex()}")
# 128-stelliger Hex-String
# URL-sicheres Base64 — kompakt, sicher für HTTP-Header
import base64
b64_key = base64.urlsafe_b64encode(sha256_key).decode("ascii")
print(f"Base64-Schlüssel: {b64_key}")
# z. B. "o_G5wE59L4obPF1-nwortG2ODwobPExdXnqLnA0dLi8="random.random() oder random.randbytes() aus dem random Modul für HMAC-Schlüssel. Das Standard- random Modul verwendet den Mersenne-Twister-PRNG, der nach 624 Ausgaben vorhersagbar ist. Verwende immer secrets.token_bytes() für sicherheitssensitive Zufälligkeit.Schlüssellänge und RFC 2104-Anforderungen
RFC 2104 legt fest, dass der HMAC-Schlüssel beliebig lang sein kann, empfiehlt aber mindestens L Bytes — wobei L die Ausgabelänge der zugrunde liegenden Hash-Funktion ist. Für HMAC-SHA256 sind das 32 Bytes (256 Bits). Schlüssel kürzer als L Bits reduzieren die Sicherheitsmarge proportional. Schlüssel länger als die Blockgröße des Hashs (64 Bytes für SHA-256, 128 Bytes für SHA-512) werden zunächst auf die Blockgröße gehasht, sodass es keinen Vorteil bringt, Schlüssel länger als die Blockgröße zu verwenden. Bleib bei 32 Bytes für HMAC-SHA256 und 64 Bytes für HMAC-SHA512.
Sichere Schlüsselspeicherung und -rotation
Hardcode niemals HMAC-Schlüssel im Quellcode. Der Standardansatz für Produktiv- deployments ist, den Schlüssel beim Start aus einer Umgebungsvariable zu laden: os.environ["HMAC_SECRET"].encode(). Für Umgebungen mit höheren Sicherheitsanforderungen speichere Schlüssel in einem Secrets-Management-System wie AWS Secrets Manager, HashiCorp Vault oder GCP Secret Manager und lade sie zur Laufzeit. Diese Systeme bieten Audit-Logs, Zugriffskontrollen und automatische Rotation ohne Code-Deployment.
Plane Schlüsselrotation von Anfang an ein. Bei einer Schlüsselrotation gibt es ein Zeitfenster, in dem laufende Anfragen mit dem alten Schlüssel signiert wurden und bei der Verifizierung gegen den neuen Schlüssel fehlschlagen. Die Standardmaßnahme ist eine kurze Überlappungsphase: Signaturen sowohl des alten als auch des neuen Schlüssels für kurze Zeit (Minuten bis Stunden) akzeptieren, dann den alten Schlüssel außer Betrieb nehmen. Wenn ein Schlüssel kompromittiert wurde — in Logs exponiert, durch einen Git-Commit durchgesickert oder bei einem Sicherheitsvorfall offengelegt — sofort rotieren und alle mit dem kompromittierten Schlüssel erzeugten Signaturen als nicht vertrauenswürdig behandeln. Gecachte Verifizierungsergebnisse neu prüfen und nachgelagerte Konsumenten über die Schlüsseländerung benachrichtigen.
bytearray und memoryview mit hmac.new() verwenden
Die hmac.new() Funktion akzeptiert für die key- und msg-Parameter jedes bytes-artige Objekt. Das bedeutet, du kannst bytes, bytearray oder memoryview direkt übergeben, ohne zu kopieren oder zu konvertieren. Das ist am wichtigsten in zwei Szenarien: Netzwerkprotokoll-Implementierungen, bei denen socket.recv_into() Daten in einen vorab allokierten bytearray Puffer schreibt, und Hochdurchsatz-Systemen, bei denen das Vermeiden von Zwischenkopien den GC-Druck reduziert. Ein memoryview Slice ist kopierlos: Er stellt ein Fenster in den ursprünglichen Puffer bereit, ohne neuen Speicher zu allozieren. Bei zehntausenden von Nachrichten pro Sekunde macht das Eliminieren dieser Allokationen einen messbaren Unterschied in Latenz und Durchsatz.
import hmac
import hashlib
# bytearray — veränderliche Bytes, nützlich für binäre Protokollpuffer
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-Signatur: {sig[:32]}...")
# memoryview — kopierloser Ausschnitt eines größeren Puffers
large_buffer = bytearray(4096)
large_buffer[:20] = b"sensor_reading_12345"
# HMAC nur der ersten 20 Bytes ohne Kopieren
view = memoryview(large_buffer)[:20]
sig = hmac.new(key, view, hashlib.sha256).hexdigest()
print(f"Sensor-Signatur: {sig[:32]}...")Häufige Fehler
Die ersten beiden Fehler sehe ich in fast jedem Code-Review von Webhook-Handlern. Sie sind leicht unter Zeitdruck einzuführen und schwer zu erkennen, wenn man nicht weiß, worauf man achten muss.
Problem: Der ==-Operator schließt beim ersten Byte-Unterschied kurz und gibt Timing-Informationen preis, die es einem Angreifer ermöglichen, die erwartete Signatur schrittweise zu rekonstruieren.
Lösung: Immer hmac.compare_digest() für Signaturvergleiche verwenden — es läuft in konstanter Zeit, unabhängig davon, wo die Nichtübereinstimmung auftritt.
received_sig = request.headers["X-Signature"]
expected_sig = hmac.new(key, body, hashlib.sha256).hexdigest()
if received_sig == expected_sig: # ANFÄLLIG für Timing-Angriff
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): # zeitkonstant
process_webhook(body)Problem: hmac.new() erfordert bytes-artige Objekte. Das Übergeben eines Python-str wirft TypeError: "key: expected bytes or bytearray, but got 'str'".
Lösung: Für String-Schlüssel und Nachrichten .encode() aufrufen, bevor sie an hmac.new() übergeben werden.
key = "my_api_secret" # str, nicht bytes
msg = '{"event":"test"}' # str, nicht 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)Problem: Der Aufruf von hmac.new(key, msg) ohne das dritte Argument wirft TypeError. Vor Python 3.4 war MD5 der Standard, aber der Standard wurde aus Sicherheitsgründen entfernt.
Lösung: Den Algorithmus immer explizit übergeben: hashlib.sha256, hashlib.sha512 oder was das Protokoll erfordert.
# Fehlendes digestmod — wirft TypeError in Python 3.4+ sig = hmac.new(key, msg).hexdigest()
# Hash-Algorithmus immer angeben sig = hmac.new(key, msg, hashlib.sha256).hexdigest()
Problem: Viele Webhook-Anbieter (Stripe, Shopify) senden Base64-kodierte Signaturen, kein Hex. Der Vergleich eines Hex-Strings mit einem Base64-Wert schlägt immer fehl und führt dazu, dass alle Webhooks abgelehnt werden.
Lösung: Die Anbieterdokumentation auf das Signaturformat prüfen. Bei Base64 die rohen .digest()-Bytes kodieren, nicht den .hexdigest()-String.
# Anbieter sendet Base64, wir berechnen Hex — stimmt nie überein expected = hmac.new(key, body, hashlib.sha256).hexdigest() # "a3f1b9c0..." vs "o/G5wE59..." — immer Nichtübereinstimmung
import base64
# Format des Anbieters verwenden: rohe Bytes → Base64
raw = hmac.new(key, body, hashlib.sha256).digest()
expected = base64.b64encode(raw).decode("ascii")
# "o/G5wE59..." — stimmt mit dem Header übereinstdlib hmac vs cryptography — Kurzvergleich
Das stdlib- hmac Modul für Webhook-Verifizierung, API-Signierung und allgemeine HMAC-Operationen verwenden — es erfordert keine Abhängigkeiten und deckt jeden Standard-Algorithmus ab. Für Batch-Operationen, bei denen die einmalige Geschwindigkeit wichtig ist, hmac.digest() verwenden. Die cryptography Bibliothek nur einsetzen, wenn sie bereits für andere Operationen (TLS, X.509, symmetrische Verschlüsselung) benötigt wird und die ausnahmebasierte .verify() API erwünscht ist. Für schnelle Signaturprüfungen ohne Python-Code nutze den HMAC-Generator, um Schlüssel und Nachricht einzufügen und sofort das Ergebnis zu erhalten.
Häufig gestellte Fragen
Was ist der Unterschied zwischen hmac.new() und hmac.digest() in Python?
hmac.new() gibt ein HMAC-Objekt zurück, das inkrementelle .update()-Aufrufe unterstützt und sowohl .digest() (rohe Bytes) als auch .hexdigest() (Hex-String) bereitstellt. hmac.digest() ist eine einmalige Funktion, die in Python 3.7 hinzugefügt wurde und rohe Bytes direkt zurückgibt, ohne ein Zwischenobjekt zu erzeugen. Verwende hmac.digest(), wenn du die vollständige Nachricht hast und nur das Ergebnis benötigst. Verwende hmac.new(), wenn du Daten in Blöcken verarbeiten oder eine Hex-Ausgabe benötigst.
import hmac, hashlib
key = b"webhook_secret_2026"
body = b'{"event":"payment.completed","amount":9950}'
# Einmalig — gibt rohe Bytes zurück
raw = hmac.digest(key, body, hashlib.sha256)
# Objekt-basiert — unterstützt inkrementelle Updates und Hex-Ausgabe
h = hmac.new(key, body, hashlib.sha256)
hex_sig = h.hexdigest()Wie verifiziere ich eine HMAC-Signatur in Python?
Berechne den HMAC der ursprünglichen Nachricht mit dem gemeinsamen Geheimnis neu und vergleiche dann mit hmac.compare_digest(). Verwende niemals == für den Signaturvergleich. Der ==-Operator schließt beim ersten nicht übereinstimmenden Byte kurz und gibt so Informationen über die erwartete Signaturlänge und den Inhalt preis.
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)Ist Python hmac SHA-256 dasselbe wie ein Hash mit hashlib.sha256?
Nein. hashlib.sha256 berechnet einen einfachen SHA-256-Hash der Eingabe, den jeder reproduzieren kann. HMAC-SHA256 mischt gemäß RFC 2104 einen geheimen Schlüssel in die Hash-Berechnung ein, sodass nur jemand mit dem Schlüssel die korrekte Ausgabe erzeugen oder verifizieren kann. Ein einfacher Hash beweist die Datenintegrität. Ein HMAC beweist sowohl Integrität als auch Authentizität.
import hmac, hashlib msg = b"transfer:9950:USD" key = b"api_secret_k8x2" plain_hash = hashlib.sha256(msg).hexdigest() # jeder kann das berechnen hmac_hash = hmac.new(key, msg, hashlib.sha256).hexdigest() # erfordert den Schlüssel
Kann ich HMAC-SHA1 in Python 3 verwenden?
Ja, übergib hashlib.sha1 als digestmod-Argument. HMAC-SHA1 funktioniert in Python 3 problemlos, und das hmac-Modul zeigt dafür keine Deprecation-Warnungen. Dennoch gilt SHA-1 für neue Entwürfe als schwach — seine Kollisionsresistenz liegt unter 80 Bit und NIST hat es 2015 für die meisten digitalen Signaturanwendungen als veraltet erklärt. Der Hauptgrund für HMAC-SHA1 heute ist die Abwärtskompatibilität mit bestehenden Protokollen, die es vorschreiben, wie OAuth 1.0a oder bestimmte ältere Webhook-Systeme. Wenn du beide Seiten der Integration kontrollierst, bevorzuge für neue Projekte HMAC-SHA256 oder HMAC-SHA512.
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()
Wie generiere ich einen sicheren HMAC-Schlüssel in Python?
Verwende secrets.token_bytes() aus der Standardbibliothek. Für HMAC-SHA256 ist ein 32-Byte-Schlüssel die Standardempfehlung, da er der Hash-Blockgröße entspricht. Für HMAC-SHA512 verwende 64 Bytes. Verwende nicht random.random() oder os.urandom() für die Schlüsselgenerierung in Anwendungscode — secrets ist das richtige Modul für sicherheitssensitive Zufälligkeit seit Python 3.6.
import secrets hmac_sha256_key = secrets.token_bytes(32) # 256 Bits hmac_sha512_key = secrets.token_bytes(64) # 512 Bits # Als Hex für Konfigurationsdateien speichern print(hmac_sha256_key.hex()) # z. B. "a3f1b9c04e..."
Warum erfordert hmac.new() digestmod in Python 3?
Vor Python 3.4 war der Standardwert von digestmod MD5, das kryptografisch gebrochen ist — MD5 hat bekannte Kollisionsangriffe und sollte niemals in neuem sicherheitssensitivem Code verwendet werden. Die Python-Maintainer haben den Standard entfernt, um eine explizite Algorithmuswahl zu erzwingen und zu verhindern, dass Entwickler versehentlich MD5-basierte MACs ausliefern. Wenn du hmac.new(key, msg) ohne digestmod aufrufst, erhältst du einen TypeError. Übergib den Algorithmus immer explizit: hashlib.sha256, hashlib.sha512 oder einen anderen hashlib-Konstruktor. Im Zweifelsfall ist hashlib.sha256 die sichere Wahl — keine bekannten Schwachstellen und schnell genug für jede praktische Arbeitslast.
import hmac, hashlib key = b"secret" msg = b"data" # Wirft TypeError in Python 3.4+ # hmac.new(key, msg) # TypeError: missing required argument: 'digestmod' # Algorithmus immer angeben h = hmac.new(key, msg, hashlib.sha256)
Für eine schnelle HMAC-Prüfung ohne ein Python-Skript zu starten, füge deinen Schlüssel und deine Nachricht in den Online-HMAC-Generator ein — er unterstützt SHA-256, SHA-384 und SHA-512 mit sofortigen Ergebnissen.
Verwandte 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.