HMAC Python — hmac.new() SHA-256 guide med kodexempel
Använd det kostnadsfria HMAC Generator direkt i webbläsaren — ingen installation krävs.
Prova HMAC Generator online →Varje webhook-anrop, varje signerad API-begäran, varje Stripe- eller GitHub-händelse använder en HMAC-signatur för att bevisa att nyttolasten inte manipulerats. Pythons hmac -modul hanterar HMAC-SHA256 i Python med ett enda funktionsanrop: hmac.new(key, msg, hashlib.sha256). Ingen pip install, ingen C-extension, inget tredjepartsberoende. För snabba engångssignaturer utan att skriva kod ger HMAC-generatorn online resultatet direkt. Den här guiden täcker hmac.new(), hmac.digest(), hmac.compare_digest(), Base64-kodning, webhook-verifiering, API-begäransignering och alla hashalgoritmer från SHA-1 till SHA-512. Alla exempel riktar sig mot Python 3.7+.
- ✓hmac.new(key, msg, hashlib.sha256) är standardingångspunkten — key och msg måste vara bytes, digestmod är obligatorisk sedan Python 3.4.
- ✓hmac.digest(key, msg, "sha256") är ett snabbare engångsalternativ tillagt i Python 3.7 — returnerar råa bytes, inget mellanliggande objekt.
- ✓Verifiera alltid signaturer med hmac.compare_digest() för att förhindra timingangrepp — använd aldrig == för HMAC-jämförelse.
- ✓Base64-koda den råa .digest()-utdatan för HTTP-headers och webhook-signaturer: base64.b64encode(h.digest()).
- ✓hmac-modulen accepterar valfri hashlib-algoritm: sha1, sha256, sha384, sha512, md5, blake2b.
Vad är HMAC?
HMAC (Hash-based Message Authentication Code) är en konstruktion definierad i RFC 2104 som kombinerar en hemlig nyckel med en hashfunktion för att producera en autentiseringstagg av fast storlek. Till skillnad från en enkel hash (som vem som helst kan beräkna) kräver ett HMAC kännedom om den hemliga nyckeln. Det innebär att du kan använda det för att verifiera både integriteten och autenticiteten hos ett meddelande. Om ens en enda byte av meddelandet eller nyckeln ändras är utdatan helt annorlunda. Konstruktionen fungerar genom att hasha nyckeln XOR:ad med två olika utfyllnadskonstanter (ipad och opad), och omsluter meddelandet mellan två hashoperationer. Pythons hmac -modul implementerar denna RFC direkt.
# Enkel SHA-256-hash — ingen hemlig nyckel, vem som helst kan beräkna hashlib.sha256(b"payment:9950:USD").hexdigest() # "7a3b1c..." (deterministisk, offentlig)
# HMAC-SHA256 — kräver den hemliga nyckeln för att producera hmac.new(b"api_secret", b"payment:9950:USD", hashlib.sha256).hexdigest() # "e4f2a8..." (bara nyckelinnehavaren kan beräkna)
hmac.new() — standardbibliotekets ingångspunkt
Modulen hmac ingår i Pythons standardbibliotek. Två importer och du är redo: import hmac, hashlib. De tre huvudfunktionerna är hmac.new() (skapar ett HMAC-objekt), hmac.digest() (engångsanrop, Python 3.7+) och hmac.compare_digest() (konstanttidsjämförelse). Ingen pip install krävs.
hmac.new(key, msg, digestmod) tar tre argument. Både key och msg måste vara bytes-liknande objekt ( bytes, bytearray eller memoryview). Argumentet digestmod är obligatoriskt sedan Python 3.4 och accepterar valfri hashlib -konstruktor (som hashlib.sha256) eller ett strängnamn som "sha256".
import hmac
import hashlib
key = b"webhook_signing_key_2026"
message = b'{"event":"invoice.paid","invoice_id":"inv_8f3a","amount":19900}'
# Skapa HMAC-objektet och hämta hex-signaturen
signature = hmac.new(key, message, hashlib.sha256).hexdigest()
print(signature)
# "b4e74f6c9a1d3e5f8b2a7c0d4e6f1a3b5c7d9e0f2a4b6c8d0e1f3a5b7c9d0e2f"HMAC-objektet exponerar två utdatametoder. .digest() returnerar råa bytes (32 bytes för SHA-256, 64 för SHA-512). .hexdigest() returnerar en hexsträng med gemener. Hexsträngen är en vanlig Python str — inget avkodningssteg behövs.
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 # De representerar samma data — hex är bara en strängkodning av bytes assert raw_bytes.hex() == hex_string
Om din nyckel eller ditt meddelande är en Python-sträng, anropa .encode() för att konvertera den till bytes innan du skickar den till hmac.new(). Det här snubblar nästan alla på första gången — Python 3-strängar är Unicode, inte bytes, och hmac-modulen vägrar dem.
import hmac
import hashlib
# Strängnyckel och meddelande — .encode() konverterar till 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..." — konsekvent hexsträngsutdatadigestmod har inget standardvärde sedan Python 3.4. Att anropa hmac.new(key, msg) utan den ger TypeError. Före 3.4 var standardvärdet MD5, vilket är varför Python-underhållarna tog bort standardvärdet — det tvingar dig att göra ett explicit, säkert val.HMAC-SHA256 Base64, SHA-1, SHA-512 och MD5
Funktionen hmac.new() fungerar med valfri hashalgoritm tillgänglig i hashlib. De flesta webhook-leverantörer och API-gateways använder HMAC-SHA256, men du stöter på SHA-1 i OAuth 1.0a, SHA-512 i protokoll som kräver det, och MD5 i äldre system som inte uppdaterats.
HMAC-SHA256 med Base64-utdata
Många webhook-leverantörer skickar signaturen som en Base64-kodad sträng i ett HTTP-header. För att producera samma format, skicka de råa .digest() -bytes till base64.b64encode().
import hmac
import hashlib
import base64
key = b"whsec_MbkP7x9yFqHGn3tRdWz5"
payload = b'{"id":"evt_1Nq","type":"charge.succeeded","data":{"amount":4200}}'
# Råa digest → Base64 (vanligt för Authorization-headers och webhook-signaturer)
raw_digest = hmac.new(key, payload, hashlib.sha256).digest()
b64_signature = base64.b64encode(raw_digest).decode("ascii")
print(b64_signature)
# "dGhpcyBpcyBhIHNhbXBsZSBzaWduYXR1cmU="
# Det här är värdet du jämför mot X-Signature-headern
header_value = f"sha256={b64_signature}"
print(header_value)
# "sha256=dGhpcyBpcyBhIHNhbXBsZSBzaWduYXR1cmU="HMAC-SHA1 — bakåtkompatibilitet med äldre protokoll
SHA-1 anses svagt för ny design, men HMAC-SHA1 krävs fortfarande av OAuth 1.0a och vissa äldre webhook-implementationer. Koden är identisk — byt bara ut algoritmen.
import hmac
import hashlib
consumer_secret = b"oauth_consumer_secret_2026"
token_secret = b"oauth_token_secret_2026"
# OAuth 1.0a använder consumer_secret&token_secret som signeringsnyckel
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-koda detta för Authorization-headernHMAC-SHA512 — längre utdata
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 bitar)
print(len(h.hexdigest())) # 128 hexadecimala tecken
print(h.hexdigest()[:40] + "...")
# "8e3a1f9b2c4d6e7f0a1b3c5d7e9f0a2b4c6d8e0f..."HMAC-MD5 — endast för äldre system
import hmac import hashlib # MD5 är kryptografiskt trasigt — använd endast för bakåtkompatibilitet med äldre protokoll 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-teckens hexsträng # "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
Parameterreferens för hmac.new()
Konstruktorsignaturen är hmac.new(key, msg=None, digestmod). Alla tre funktioner i modulen delar samma mönster för nyckel- och algoritmargument.
hmac.new() konstruktor
hmac.digest() engångsanrop (Python 3.7+)
Parametern digestmod accepterar antingen ett anropsbart objekt (som hashlib.sha256) eller ett strängnamn (som "sha256"). Det anropsbara formatet är att föredra eftersom det valideras vid importtillfället — ett stavfel i strängformatet ger bara fel vid körning.
hmac.digest() — snabbt engångs-HMAC (Python 3.7+)
Python 3.7 lade till hmac.digest(key, msg, digest) som en modulnivåfunktion. Den beräknar HMAC i ett enda anrop utan att skapa ett mellanliggande HMAC-objekt. Returvärdet är råa bytes (motsvarar att anropa .digest() på objektet). Funktionen använder en optimerad C-implementation på CPython och undviker objektallokeringsomkostnaden, vilket gör den mätbart snabbare i täta loopar.
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}',
]
# Engångsdigest — inget mellanliggande HMAC-objekt
signatures = [hmac.digest(key, msg, hashlib.sha256) for msg in messages]
# Konvertera till hex för visning
for msg, sig in zip(messages, signatures):
print(f"{msg[:30]}... -> {sig.hex()[:24]}...")Begränsningen: hmac.digest() returnerar bara råa bytes. Om du behöver hexsträngen direkt behöver du fortfarande hmac.new() för dess .hexdigest() -metod, eller kedja .hex() på bytes-resultatet.
hmac.digest() stöder inte inkrementella .update()-anrop. Om du läser en stor fil i segment och behöver HMAC:a innehållet, använd hmac.new() och anropa .update(chunk) i en loop.Verifiera HMAC-signatur från webhook och API-svar
Den vanligaste användningen av HMAC i Python är att verifiera webhook-signaturer. Varje stor leverantör (Stripe, GitHub, Shopify, Twilio) signerar nyttolaster med HMAC-SHA256 och skickar signaturen i ett header. Mönstret är alltid detsamma: beräkna om HMAC över det råa begäranssättet, jämför sedan med hmac.compare_digest().
Verifiering av webhook-signatur
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():
# Hämta den råa kroppen — måste matcha exakt vad som signerades
raw_body = request.get_data()
# Hämta signaturen från headern
received_sig = request.headers.get("X-Signature-256", "")
# Beräkna om HMAC över den råa kroppen
expected_sig = hmac.new(WEBHOOK_SECRET, raw_body, hashlib.sha256).hexdigest()
# Konstanttidsjämförelse — förhindrar timingangrepp
if not hmac.compare_digest(f"sha256={expected_sig}", received_sig):
abort(403, "Invalid signature")
# Signatur verifierad — behandla händelsen
event = request.get_json()
print(f"Verified event: {event['type']} for {event['data']['amount']}")
return "", 200Funktionen hmac.compare_digest() jämför två strängar eller byte-sekvenser i konstant tid. En vanlig == -jämförelse kortsluter vid den första felaktiga byten. En angripare kan mäta svarstiden över många begäran och gradvis rekonstruera den förväntade signaturen byte för byte. Konstanttidsjämförelse eliminerar denna sidokanal.
GitHub-webhook-verifiering
GitHubs webhook-format illustrerar det fullständiga mönstret. Det skickar ett X-Hub-Signature-256 -header som innehåller sha256= följt av hex-kodad HMAC-SHA256 av den råa begäranskroppen, signerad med webhook-hemligheten du konfigurerar i dina repository-inställningar. Den viktigaste skillnaden från generell webhook-verifiering är att du måste ta bort sha256= -prefixet innan jämförelse, och du måste läsa de råa bytes från begäranskroppen — att tolka JSON först ändrar byte-representationen och bryter verifieringen.
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 skickar: 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() # råa bytes — tolka inte JSON före detta
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 "", 200Samma mönster gäller för Shopify (X-Shopify-Hmac-SHA256) och Twilio (X-Twilio-Signature), med den enda skillnaden att headernamnet och huruvida signaturen är hex- eller Base64-kodad varierar. Kontrollera alltid leverantörens dokumentation för att bekräfta kodningsformatet — att blanda ihop hex och Base64 är den vanligaste orsaken till signaturmismatch-fel.
API-begäransautentisering med HMAC
Vissa API:er kräver att klienten signerar varje begäran med HMAC. Den signerade strängen innehåller vanligtvis HTTP-metoden, sökvägen, tidsstämpeln och begäranskroppen. Här är ett mönster jag använder för intern tjänst-till-tjänst-autentisering.
import hmac
import hashlib
import time
import json
def sign_request(secret: bytes, method: str, path: str, body: str) -> dict:
"""Generera HMAC-SHA256-signatur för en API-begäran."""
timestamp = str(int(time.time()))
# Bygg signeringssträngen — metod + sökväg + tidsstämpel + kropp
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,
}
# Användning
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..."}Signera HTTP-begäran med requests-biblioteket
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=(",", ":")) # kompakt JSON, deterministisk
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
# Skicka en signerad POST-begäran
resp = make_signed_request("POST", "/api/v2/invoices", {
"customer_id": "cust_4421",
"line_items": [
{"description": "Pro plan - mars 2026", "amount": 4900},
{"description": "Extra platser (3)", "amount": 2100},
],
})
print(resp.status_code, resp.json())En snabb notering: använd separators=(",", ":") när du serialiserar kroppen för signering. Standard- json.dumps() lägger till mellanslag efter avgränsare, vilket ändrar byte-representationen och bryter signaturverifieringen om servern serialiserar annorlunda. Kompakt JSON ger dig en kanonisk form.
HMAC-generering via kommandoraden
Ibland behöver du beräkna ett HMAC utan att skriva ett skript. Pythons -c -flagga och openssl hanterar båda detta från terminalen.
python3 -c " import hmac, hashlib print(hmac.new(b'my_secret', b'message_to_sign', hashlib.sha256).hexdigest()) " # utdata: 64-teckens hexsträng
echo -n "message_to_sign" | openssl dgst -sha256 -hmac "my_secret" # SHA2-256(stdin)= 7d11... # Pipa en fil genom openssl HMAC openssl dgst -sha256 -hmac "my_secret" < payload.json
# Lagra nyckeln i miljövariabel för att undvika exponering i shell-historik
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 är kritisk — utan den lägger echo till ett radsluttecken i meddelandet, vilket ändrar HMAC-utdatan. Det är den vanligaste orsaken till signaturmismatch vid felsökning från terminalen.Högprestandaalternativ — cryptography-biblioteket
För de flesta applikationer är standard- hmac -modulen tillräckligt snabb. Om du redan använder cryptography -biblioteket för TLS eller certifikathantering erbjuder det också HMAC backat av OpenSSL. Den viktigaste praktiska skillnaden från stdlib är det undantagsbaserade .verify() -API:t beskrivet nedan — det kastar undantag vid mismatch istället för att returnera ett booleskt värde som du kanske glömmer att kontrollera.
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() # råa bytes
print(signature.hex())
# "9c4e2a..."
# Verifieringsläge — kastar InvalidSignature vid mismatch
h_verify = crypto_hmac.HMAC(key, hashes.SHA256())
h_verify.update(message)
h_verify.verify(signature) # kastar cryptography.exceptions.InvalidSignature om felMetoden .verify() i cryptography -biblioteket är särskilt praktisk: den kastar ett undantag vid mismatch istället för att returnera ett booleskt värde. Det gör det svårare att av misstag ignorera ett verifieringsfel. Standardbibliotekets hmac.compare_digest() returnerar True/ False, vilket kan ignoreras tyst om utvecklaren glömmer att kontrollera returvärdet.
Terminalutdata med syntaxmarkering
Om du felsöker HMAC-signaturer i terminalen och vill ha färgad utdata hanterar rich detta bra.
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)Arbeta med stora filer — inkrementellt HMAC
För filer över ungefär 50 MB är det slösaktigt att läsa in allt i minnet bara för att beräkna ett HMAC. Metoden .update() på HMAC-objektet låter dig mata in data i segment. Detta håller minnesanvändningen konstant oavsett filstorlek.
import hmac
import hashlib
def hmac_file(key: bytes, filepath: str, chunk_size: int = 8192) -> str:
"""Beräkna HMAC-SHA256 för en fil utan att läsa in den helt i minnet."""
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()
# Signera en 2 GB databasexport
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:
"""Verifiera HMAC-SHA256-signaturen för en fil."""
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)
# Verifiera en nedladdad artefakt
is_valid = verify_file_hmac(
key=b"release_signing_key",
filepath="/tmp/release-v3.2.0.tar.gz",
expected_hex="8e3a1f9b2c4d6e7f0a1b3c5d7e9f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f",
)
print(f"Filintegritet: {'giltig' if is_valid else 'SKADAD'}").update() använder ett fast chunk_size i minnet oavsett filstorlek. Jag använder 64 KB-segment som standard — tillräckligt stort för att amortisera syscall-omkostnader, tillräckligt litet för att rymmas i L2-cache på de flesta processorer.Generera en kryptografiskt säker HMAC-nyckel i Python
En svag eller förutsägbar nyckel undergräver hela HMAC-konstruktionen. Modulen secrets (Python 3.6+) tillhandahåller kryptografiskt starka slumpmässiga bytes. För HMAC-SHA256, använd en 32-byte nyckel. För HMAC-SHA512, använd 64 bytes. Dessa matchar den interna blockstorleken för respektive hashalgoritm, vilket är den optimala nyckellängden enligt RFC 2104.
import secrets
# Generera nycklar som matchar hashalgoritmen blockstorlek
sha256_key = secrets.token_bytes(32) # 256 bitar — för HMAC-SHA256
sha512_key = secrets.token_bytes(64) # 512 bitar — för HMAC-SHA512
# Hexrepresentation — säker för konfigurationsfiler och miljövariabler
print(f"HMAC-SHA256-nyckel: {sha256_key.hex()}")
# t.ex. "a3f1b9c04e7d2f8a1b3c5d7e9f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f"
print(f"HMAC-SHA512-nyckel: {sha512_key.hex()}")
# 128-teckens hexsträng
# URL-säker Base64 — kompakt, säker för HTTP-headers
import base64
b64_key = base64.urlsafe_b64encode(sha256_key).decode("ascii")
print(f"Base64-nyckel: {b64_key}")
# t.ex. "o_G5wE59L4obPF1-nwortG2ODwobPExdXnqLnA0dLi8="random.random() eller random.randbytes() från random-modulen för HMAC-nycklar. Standard- random-modulen använder Mersenne Twister-PRNG, som är förutsägbar efter att ha observerat 624 utdatavärden. Använd alltid secrets.token_bytes() för säkerhetskänslig slumpmässighet.Nyckellängd och RFC 2104-krav
RFC 2104 specificerar att HMAC-nyckeln kan ha valfri längd, men rekommenderar en nyckel på minst L bytes — där L är utdatalängden för den underliggande hashfunktionen. För HMAC-SHA256 är det 32 bytes (256 bitar). Nycklar kortare än L bitar minskar säkerhetsmarginalen proportionellt. Nycklar längre än hashens blockstorlek (64 bytes för SHA-256, 128 bytes för SHA-512) hashas först ner till blockstorleken, så det finns ingen fördel med att använda nycklar längre än blockstorleken. Håll dig till 32 bytes för HMAC-SHA256 och 64 bytes för HMAC-SHA512.
Säker nyckellagring och rotation
Hårdkoda aldrig HMAC-nycklar i källkod. Standardmetoden för produktionsdriftsättningar är att läsa in nyckeln från en miljövariabel vid uppstart: os.environ["HMAC_SECRET"].encode(). För miljöer med högre säkerhetskrav, lagra nycklar i ett hemlighethanteringssystem som AWS Secrets Manager, HashiCorp Vault eller GCP Secret Manager och hämta dem vid körning. Dessa system tillhandahåller granskningsloggar, åtkomstkontroller och automatisk rotation utan att kräva en koddriftsättning.
Planera för nyckelrotation från början. När en nyckel roteras finns det ett fönster där pågående begäran signerades med den gamla nyckeln och misslyckas med verifiering mot den nya. Standardlösningen är en kort överlappningsperiod: acceptera signaturer från både den gamla och nya nyckeln under en kort tid (minuter till timmar), och dra sedan tillbaka den gamla nyckeln. Om en nyckel komprometteras — exponeras i loggar, läcker via en git-commit eller avslöjas i en incident — rotera omedelbart och betrakta alla signaturer producerade med den komprometterade nyckeln som opålitliga. Omverifiera alla cachade verifieringsresultat och meddela nedströmskonsumenter om nyckeländringen.
Använda bytearray och memoryview med hmac.new()
Funktionen hmac.new() accepterar valfritt bytes-liknande objekt för både nyckel- och msg-parametrarna. Det innebär att du kan skicka bytes, bytearray eller memoryview direkt, utan kopiering eller konvertering. Det spelar mest roll i två scenarier: nätverksprotokollimplementationer där socket.recv_into() skriver data till en förallokerad bytearray -buffert, och system med hög genomströmning där undvikande av mellanliggande kopior minskar GC-trycket. En memoryview -slice är nollkopiering: den exponerar ett fönster in i den ursprungliga bufferten utan att allokera nytt minne. Vid tiotusentals meddelanden per sekund ger elimineringen av dessa allokeringar en mätbar skillnad i latens och genomströmning.
import hmac
import hashlib
# bytearray — föränderliga bytes, användbara för binära protokollbuffertar
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"Ramsignatur: {sig[:32]}...")
# memoryview — nollkopieringsslice av en större buffert
large_buffer = bytearray(4096)
large_buffer[:20] = b"sensor_reading_12345"
# HMAC bara de första 20 bytes utan kopiering
view = memoryview(large_buffer)[:20]
sig = hmac.new(key, view, hashlib.sha256).hexdigest()
print(f"Sensorsignatur: {sig[:32]}...")Vanliga misstag
De två första misstagen ser jag i nästan varje kodgranskning som involverar webhook-hanterare. De är lätta att introducera under tidspress och svåra att upptäcka utan att veta vad man letar efter.
Problem: Operatorn == kortsluter vid den första byte-mismatch och läcker timinginformation som låter en angripare rekonstruera den förväntade signaturen inkrementellt.
Lösning: Använd alltid hmac.compare_digest() för signaturrjämförelse — den körs i konstant tid oavsett var mismatchen inträffar.
received_sig = request.headers["X-Signature"]
expected_sig = hmac.new(key, body, hashlib.sha256).hexdigest()
if received_sig == expected_sig: # SÅRBAR för timingangrepp
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): # konstanttid
process_webhook(body)Problem: hmac.new() kräver bytes-liknande objekt. Att skicka en Python str ger TypeError: "key: expected bytes or bytearray, but got 'str'".
Lösning: Anropa .encode() på strängar för nycklar och meddelanden innan de skickas till hmac.new().
key = "my_api_secret" # str, inte bytes
msg = '{"event":"test"}' # str, inte 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: Att anropa hmac.new(key, msg) utan det tredje argumentet ger TypeError. Före Python 3.4 var standardvärdet MD5, men standarden togs bort av säkerhetsskäl.
Lösning: Ange alltid algoritmen explicit: hashlib.sha256, hashlib.sha512, eller vad ditt protokoll kräver.
# Saknar digestmod — ger TypeError i Python 3.4+ sig = hmac.new(key, msg).hexdigest()
# Ange alltid hashalgoritmen sig = hmac.new(key, msg, hashlib.sha256).hexdigest()
Problem: Många webhook-leverantörer (Stripe, Shopify) skickar Base64-kodade signaturer, inte hex. Att jämföra en hexsträng mot ett Base64-värde misslyckas alltid och orsakar att alla webhooks avvisas.
Lösning: Kontrollera leverantörens dokumentation för signaturformatet. Om de använder Base64, koda de råa .digest()-bytes, inte .hexdigest()-strängen.
# Leverantören skickar Base64, men vi beräknar hex — matchar aldrig expected = hmac.new(key, body, hashlib.sha256).hexdigest() # "a3f1b9c0..." vs "o/G5wE59..." — alltid mismatch
import base64
# Matcha leverantörens format: råa bytes → Base64
raw = hmac.new(key, body, hashlib.sha256).digest()
expected = base64.b64encode(raw).decode("ascii")
# "o/G5wE59..." — matchar headernstdlib hmac vs cryptography — snabb jämförelse
Använd stdlib- hmac -modulen för webhook-verifiering, API-signering och allmänna HMAC-operationer — den kräver noll beroenden och täcker alla standardalgoritmer. Använd hmac.digest() för batchoperationer där engångshastigheten spelar roll. Välj cryptography -biblioteket bara när du redan är beroende av det för andra operationer (TLS, X.509, symmetrisk kryptering) och vill ha det undantagsbaserade .verify() -API:t. För snabba signaturcheckningar utan att skriva Python, använd HMAC Generator-verktyget för att klistra in din nyckel och ditt meddelande och få resultatet direkt.
Vanliga frågor
Vad är skillnaden mellan hmac.new() och hmac.digest() i Python?
hmac.new() returnerar ett HMAC-objekt som stöder inkrementella .update()-anrop och ger både .digest() (råa bytes) och .hexdigest() (hexsträng). hmac.digest() är en engångsfunktion tillagd i Python 3.7 som returnerar råa bytes direkt utan att skapa ett mellanliggande objekt. Använd hmac.digest() när du har hela meddelandet och bara behöver resultatet. Använd hmac.new() när du behöver mata in data i segment eller behöver hex-utdata.
import hmac, hashlib
key = b"webhook_secret_2026"
body = b'{"event":"payment.completed","amount":9950}'
# Engångsanrop — returnerar råa bytes
raw = hmac.digest(key, body, hashlib.sha256)
# Objektbaserat — stöder inkrementella uppdateringar och hex-utdata
h = hmac.new(key, body, hashlib.sha256)
hex_sig = h.hexdigest()Hur verifierar jag en HMAC-signatur i Python?
Beräkna om HMAC över det ursprungliga meddelandet med den delade hemligheten, jämför sedan med hmac.compare_digest(). Använd aldrig == för signaturrjämförelse. Operatorn == är sårbar för timingangrepp eftersom den kortsluter vid den första felaktiga byten och läcker information om den förväntade signaturens längd och innehåll.
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)Är Python hmac SHA-256 detsamma som att hasha med hashlib.sha256?
Nej. hashlib.sha256 beräknar en enkel SHA-256-hash av indatan, vilket vem som helst kan reproducera. HMAC-SHA256 blandar in en hemlig nyckel i hashberäkningen enligt RFC 2104, så bara den som har nyckeln kan producera eller verifiera rätt utdata. En enkel hash bevisar dataintegritet. Ett HMAC bevisar både integritet och autenticitet.
import hmac, hashlib msg = b"transfer:9950:USD" key = b"api_secret_k8x2" plain_hash = hashlib.sha256(msg).hexdigest() # vem som helst kan beräkna detta hmac_hash = hmac.new(key, msg, hashlib.sha256).hexdigest() # kräver nyckeln
Kan jag använda HMAC-SHA1 i Python 3?
Ja, skicka hashlib.sha1 som digestmod-argument. HMAC-SHA1 fungerar fortfarande bra i Python 3 och hmac-modulen har inga avrådan för det. Det sagt är SHA-1 ansett som svagt för ny design — dess kollisionsmotstånd understiger 80 bitar och NIST fasade ut det för de flesta digitala signaturändamål 2015. Det huvudsakliga skälet att använda HMAC-SHA1 idag är bakåtkompatibilitet med befintliga protokoll som kräver det, som OAuth 1.0a eller vissa äldre webhook-system. När du kontrollerar båda sidor av integrationen, föredra HMAC-SHA256 eller HMAC-SHA512 för allt nytt arbete.
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()
Hur genererar jag en säker HMAC-nyckel i Python?
Använd secrets.token_bytes() från standardbiblioteket. För HMAC-SHA256 är en 32-byte nyckel standardrekommendationen eftersom den matchar hashens blockstorlek. För HMAC-SHA512, använd 64 bytes. Använd inte random.random() eller os.urandom() för nyckelgenerering i applikationskod — secrets är rätt modul för säkerhetskänslig slumpmässighet sedan Python 3.6.
import secrets hmac_sha256_key = secrets.token_bytes(32) # 256 bitar hmac_sha512_key = secrets.token_bytes(64) # 512 bitar # Lagra som hex för konfigurationsfiler print(hmac_sha256_key.hex()) # t.ex. "a3f1b9c04e..."
Varför kräver hmac.new() digestmod i Python 3?
Före Python 3.4 använde digestmod MD5 som standard, vilket är kryptografiskt trasigt — MD5 har kända kollisionsangrepp och bör aldrig användas i ny säkerhetskänslig kod. Python-underhållarna tog bort standardvärdet för att tvinga ett explicit algoritmval och förhindra att utvecklare tyst skickar MD5-baserade MAC:ar. Om du anropar hmac.new(key, msg) utan digestmod får du ett TypeError. Ange alltid algoritmen explicit: hashlib.sha256, hashlib.sha512, eller annan hashlib-konstruktor. I tveksamma fall är hashlib.sha256 det säkra standardvalet — inga kända svagheter och tillräckligt snabbt för alla praktiska arbetsbelastningar.
import hmac, hashlib key = b"secret" msg = b"data" # Detta ger TypeError i Python 3.4+ # hmac.new(key, msg) # TypeError: missing required argument: 'digestmod' # Ange alltid algoritmen h = hmac.new(key, msg, hashlib.sha256)
För en snabb HMAC-kontroll utan att starta ett Python-skript, klistra in din nyckel och ditt meddelande i HMAC-generatorn online — den stöder SHA-256, SHA-384 och SHA-512 med omedelbara resultat.
Relaterade verktyg
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.