HMAC Python โ Panduan hmac.new() SHA-256 + Contoh Kode
Gunakan HMAC Generator gratis langsung di browser Anda โ tidak perlu instalasi.
Coba HMAC Generator Online โSetiap callback webhook, setiap permintaan API yang ditandatangani, setiap notifikasi event Stripe atau GitHub menggunakan tanda tangan HMAC untuk membuktikan bahwa payload tidak dimodifikasi. Modul hmac bawaan Python menangani HMAC-SHA256 di Python dengan satu pemanggilan fungsi: hmac.new(key, msg, hashlib.sha256). Tidak perlu pip install, tidak perlu ekstensi C, tidak ada dependensi pihak ketiga. Untuk pemeriksaan tanda tangan sekali pakai tanpa menulis kode, HMAC Generator online memberikan hasilnya secara instan. Panduan ini mencakup hmac.new(), hmac.digest(), hmac.compare_digest(), encoding Base64, verifikasi webhook, penandatanganan permintaan API, dan setiap algoritma hash mulai dari SHA-1 hingga SHA-512. Semua contoh menargetkan Python 3.7+.
- โhmac.new(key, msg, hashlib.sha256) adalah titik masuk standar โ key dan msg harus berupa bytes, digestmod wajib sejak Python 3.4.
- โhmac.digest(key, msg, "sha256") adalah alternatif one-shot yang lebih cepat, ditambahkan di Python 3.7 โ mengembalikan raw bytes tanpa objek perantara.
- โSelalu verifikasi tanda tangan dengan hmac.compare_digest() untuk mencegah timing attack โ jangan pernah gunakan == untuk perbandingan HMAC.
- โEncode output raw .digest() ke Base64 untuk HTTP header dan tanda tangan webhook: base64.b64encode(h.digest()).
- โModul hmac menerima algoritma hashlib apa pun: sha1, sha256, sha384, sha512, md5, blake2b.
Apa itu HMAC?
HMAC (Hash-based Message Authentication Code) adalah konstruksi yang didefinisikan dalam RFC 2104 yang menggabungkan secret key dengan fungsi hash untuk menghasilkan tag autentikasi berukuran tetap. Berbeda dengan hash biasa (yang dapat dihitung oleh siapa saja), HMAC membutuhkan pengetahuan tentang secret key. Ini berarti Anda dapat menggunakannya untuk memverifikasi integritas sekaligus keaslian pesan. Jika bahkan satu byte pesan atau key berubah, outputnya akan sepenuhnya berbeda. Konstruksi ini bekerja dengan cara meng-hash key yang di-XOR dengan dua konstanta padding berbeda (ipad dan opad), membungkus pesan di antara dua operasi hash. Modul hmac Python mengimplementasikan RFC ini secara langsung.
# Hash SHA-256 biasa โ tanpa secret key, siapa saja bisa menghitung hashlib.sha256(b"payment:9950:USD").hexdigest() # "7a3b1c..." (deterministik, publik)
# HMAC-SHA256 โ membutuhkan secret key untuk menghasilkan hmac.new(b"api_secret", b"payment:9950:USD", hashlib.sha256).hexdigest() # "e4f2a8..." (hanya pemegang key yang bisa menghitung)
hmac.new() โ Titik Masuk Standard Library
Modul hmac adalah bagian dari standard library Python. Dua import dan Anda siap: import hmac, hashlib. Tiga fungsi utamanya adalah hmac.new() (membuat objek HMAC), hmac.digest() (one-shot, Python 3.7+), dan hmac.compare_digest() (perbandingan constant-time). Tidak perlu pip install.
hmac.new(key, msg, digestmod) menerima tiga argumen. Baik key maupun msg harus berupa objek bytes-like ( bytes, bytearray, atau memoryview). Argumen digestmod bersifat wajib sejak Python 3.4 dan menerima konstruktor hashlib apa pun (seperti hashlib.sha256) atau nama string seperti "sha256".
import hmac
import hashlib
key = b"webhook_signing_key_2026"
message = b'{"event":"invoice.paid","invoice_id":"inv_8f3a","amount":19900}'
# Buat objek HMAC dan dapatkan tanda tangan hex
signature = hmac.new(key, message, hashlib.sha256).hexdigest()
print(signature)
# "b4e74f6c9a1d3e5f8b2a7c0d4e6f1a3b5c7d9e0f2a4b6c8d0e1f3a5b7c9d0e2f"Objek HMAC menyediakan dua metode output. .digest() mengembalikan raw bytes (32 byte untuk SHA-256, 64 untuk SHA-512). .hexdigest() mengembalikan string hex huruf kecil. String hex adalah str Python biasa โ tidak perlu langkah decoding tambahan.
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 # Keduanya merepresentasikan data yang sama โ hex hanyalah encoding string dari bytes assert raw_bytes.hex() == hex_string
Jika key atau pesan Anda adalah string Python, panggil .encode() untuk mengonversinya ke bytes sebelum dikirim ke hmac.new(). Hampir semua orang tersandung di sini pertama kali โ string Python 3 adalah Unicode, bukan bytes, dan modul hmac menolaknya.
import hmac
import hashlib
# String key dan pesan โ .encode() mengonversi ke 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..." โ output string hex yang konsistendigestmod tidak memiliki default sejak Python 3.4. Memanggil hmac.new(key, msg) tanpanya akan memunculkan TypeError. Sebelum 3.4, default-nya adalah MD5, itulah mengapa para pengembang Python menghapus default tersebut โ memaksa Anda membuat pilihan yang eksplisit dan aman.HMAC-SHA256 Base64, SHA-1, SHA-512, dan MD5
Fungsi hmac.new() bekerja dengan algoritma hash apa pun yang tersedia di hashlib. Sebagian besar penyedia webhook dan gateway API menggunakan HMAC-SHA256, tetapi Anda akan menemui SHA-1 di OAuth 1.0a, SHA-512 di protokol yang membutuhkannya, dan MD5 di sistem lama yang belum diperbarui.
HMAC-SHA256 dengan Output Base64
Banyak penyedia webhook mengirimkan tanda tangan sebagai string yang diencoding Base64 dalam header HTTP. Untuk menghasilkan format yang sama, berikan raw bytes .digest() ke base64.b64encode().
import hmac
import hashlib
import base64
key = b"whsec_MbkP7x9yFqHGn3tRdWz5"
payload = b'{"id":"evt_1Nq","type":"charge.succeeded","data":{"amount":4200}}'
# Raw digest โ Base64 (umum untuk Authorization header dan tanda tangan webhook)
raw_digest = hmac.new(key, payload, hashlib.sha256).digest()
b64_signature = base64.b64encode(raw_digest).decode("ascii")
print(b64_signature)
# "dGhpcyBpcyBhIHNhbXBsZSBzaWduYXR1cmU="
# Ini adalah nilai yang akan dibandingkan dengan header X-Signature
header_value = f"sha256={b64_signature}"
print(header_value)
# "sha256=dGhpcyBpcyBhIHNhbXBsZSBzaWduYXR1cmU="HMAC-SHA1 โ Kompatibilitas Protokol Lama
SHA-1 dianggap lemah untuk desain baru, tetapi HMAC-SHA1 masih dibutuhkan oleh OAuth 1.0a dan beberapa implementasi webhook yang lebih lama. Kodenya identik โ cukup ganti algoritmanya.
import hmac
import hashlib
consumer_secret = b"oauth_consumer_secret_2026"
token_secret = b"oauth_token_secret_2026"
# OAuth 1.0a menggunakan consumer_secret&token_secret sebagai signing key
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-encode ini untuk Authorization headerHMAC-SHA512 โ Output Lebih Panjang
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 โ Hanya untuk Kompatibilitas
import hmac import hashlib # MD5 secara kriptografi sudah rusak โ gunakan hanya untuk kompatibilitas protokol lama 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"
Referensi Parameter hmac.new()
Tanda tangan konstruktor adalah hmac.new(key, msg=None, digestmod). Ketiga fungsi dalam modul ini berbagi pola yang sama untuk argumen key dan algoritma.
konstruktor hmac.new()
hmac.digest() one-shot (Python 3.7+)
Parameter digestmod menerima callable (seperti hashlib.sha256) atau nama string (seperti "sha256"). Bentuk callable lebih disukai karena divalidasi saat import โ kesalahan ketik dalam bentuk string hanya akan gagal saat runtime.
hmac.digest() โ HMAC One-Shot Cepat (Python 3.7+)
Python 3.7 menambahkan hmac.digest(key, msg, digest) sebagai fungsi tingkat modul. Fungsi ini menghitung HMAC dalam satu pemanggilan tanpa membuat objek HMAC perantara. Nilai yang dikembalikan adalah raw bytes (setara dengan memanggil .digest() pada objek). Fungsi ini menggunakan implementasi C yang dioptimalkan di CPython dan menghindari overhead alokasi objek, sehingga terukur lebih cepat dalam operasi berulang.
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}',
]
# One-shot digest โ tanpa objek HMAC perantara
signatures = [hmac.digest(key, msg, hashlib.sha256) for msg in messages]
# Konversi ke hex untuk ditampilkan
for msg, sig in zip(messages, signatures):
print(f"{msg[:30]}... -> {sig.hex()[:24]}...")Keterbatasannya: hmac.digest() hanya mengembalikan raw bytes. Jika Anda membutuhkan string hex secara langsung, Anda tetap membutuhkan hmac.new() untuk metode .hexdigest()-nya, atau rangkaikan .hex() pada hasil bytes.
hmac.digest() tidak mendukung pemanggilan .update() secara bertahap. Jika Anda membaca file besar dalam potongan dan perlu HMAC isinya, gunakan hmac.new() dan panggil .update(chunk) dalam perulangan.Verifikasi Tanda Tangan HMAC dari Webhook dan Respons API
Penggunaan HMAC paling umum di Python adalah memverifikasi tanda tangan webhook. Setiap penyedia besar (Stripe, GitHub, Shopify, Twilio) menandatangani payload dengan HMAC-SHA256 dan mengirimkan tanda tangan dalam sebuah header. Polanya selalu sama: hitung ulang HMAC dari raw body permintaan, lalu bandingkan dengan hmac.compare_digest().
Verifikasi Tanda Tangan Webhook
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():
# Ambil raw body โ harus sama persis dengan yang ditandatangani
raw_body = request.get_data()
# Ambil tanda tangan dari header
received_sig = request.headers.get("X-Signature-256", "")
# Hitung ulang HMAC dari raw body
expected_sig = hmac.new(WEBHOOK_SECRET, raw_body, hashlib.sha256).hexdigest()
# Perbandingan constant-time โ mencegah timing attack
if not hmac.compare_digest(f"sha256={expected_sig}", received_sig):
abort(403, "Invalid signature")
# Tanda tangan terverifikasi โ proses event
event = request.get_json()
print(f"Verified event: {event['type']} for {event['data']['amount']}")
return "", 200Fungsi hmac.compare_digest() membandingkan dua string atau urutan byte dalam waktu konstan. Perbandingan == biasa berhenti pada byte pertama yang tidak cocok. Penyerang dapat mengukur waktu respons dari banyak permintaan dan secara bertahap merekonstruksi tanda tangan yang diharapkan byte demi byte. Perbandingan constant-time menghilangkan saluran samping ini.
Verifikasi Webhook GitHub
Format webhook GitHub menggambarkan pola lengkapnya. GitHub mengirimkan header X-Hub-Signature-256 yang berisi sha256= diikuti HMAC-SHA256 yang diencoding hex dari raw body permintaan, ditandatangani dengan secret webhook yang Anda konfigurasi di pengaturan repositori. Perbedaan utama dari verifikasi webhook generik adalah Anda harus menghapus awalan sha256= sebelum membandingkan, dan Anda harus membaca raw bytes dari body permintaan โ parsing JSON terlebih dahulu mengubah representasi byte dan merusak verifikasi.
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 mengirimkan: 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() # raw bytes โ jangan parse JSON sebelum ini
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 "", 200Pola yang sama berlaku untuk Shopify (X-Shopify-Hmac-SHA256) dan Twilio (X-Twilio-Signature), dengan satu-satunya perbedaan adalah nama header dan apakah tanda tangan dalam format hex atau Base64. Selalu periksa dokumentasi penyedia untuk mengonfirmasi format encoding โ mencampurkan hex dan Base64 adalah penyebab paling umum error signature mismatch.
Autentikasi Permintaan API dengan HMAC
Beberapa API mengharuskan klien menandatangani setiap permintaan dengan HMAC. String yang ditandatangani biasanya mencakup metode HTTP, path, timestamp, dan body permintaan. Berikut adalah pola yang digunakan untuk autentikasi antar layanan internal.
import hmac
import hashlib
import time
import json
def sign_request(secret: bytes, method: str, path: str, body: str) -> dict:
"""Hasilkan tanda tangan HMAC-SHA256 untuk permintaan API."""
timestamp = str(int(time.time()))
# Buat signing string โ method + path + timestamp + 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,
}
# Penggunaan
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..."}Penandatanganan Permintaan HTTP dengan Library requests
import hmac
import hashlib
import time
import json
import requests
API_SECRET = b"sk_hmac_9f3a2b7c4d8e1a6f"
BASE_URL = "https://api.billing-service.internal"
def make_signed_request(method: str, path: str, payload: dict) -> requests.Response:
body = json.dumps(payload, separators=(",", ":")) # JSON kompak, deterministik
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
# Kirim permintaan POST bertanda tangan
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())Catatan singkat: gunakan separators=(",", ":") saat melakukan serialisasi body untuk penandatanganan. Default json.dumps() menambahkan spasi setelah pemisah, yang mengubah representasi byte dan merusak verifikasi tanda tangan jika server melakukan serialisasi secara berbeda. JSON kompak memberikan bentuk kanonik.
Pembuatan HMAC via Command Line
Terkadang Anda perlu menghitung HMAC tanpa menulis skrip. Flag -c Python dan openssl keduanya dapat menangani ini dari terminal.
python3 -c " import hmac, hashlib print(hmac.new(b'my_secret', b'message_to_sign', hashlib.sha256).hexdigest()) " # outputs: 64-character hex string
echo -n "message_to_sign" | openssl dgst -sha256 -hmac "my_secret" # SHA2-256(stdin)= 7d11... # Pipe file melalui HMAC openssl openssl dgst -sha256 -hmac "my_secret" < payload.json
# Simpan key di env var agar tidak terekspos di riwayat shell
export HMAC_KEY="sk_live_9f3a2b"
echo -n '{"event":"test"}' | python3 -c "
import hmac, hashlib, sys, os
key = os.environ['HMAC_KEY'].encode()
msg = sys.stdin.buffer.read()
print(hmac.new(key, msg, hashlib.sha256).hexdigest())
"echo -n sangat penting โ tanpanya, echo menambahkan karakter newline ke pesan, yang mengubah output HMAC. Ini adalah penyebab paling umum signature mismatch saat melakukan debugging dari terminal.Alternatif Berkinerja Tinggi โ Library cryptography
Untuk sebagian besar aplikasi, modul hmac standar sudah cukup cepat. Jika Anda sudah menggunakan library cryptography untuk TLS atau penanganan sertifikat, library ini juga menyediakan HMAC yang didukung OpenSSL. Perbedaan praktis utama dari stdlib adalah API .verify() berbasis exception yang dijelaskan di bawah โ ia memunculkan exception saat tidak cocok alih-alih mengembalikan boolean yang mungkin lupa Anda periksa.
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() # raw bytes
print(signature.hex())
# "9c4e2a..."
# Mode verifikasi โ memunculkan InvalidSignature jika tidak cocok
h_verify = crypto_hmac.HMAC(key, hashes.SHA256())
h_verify.update(message)
h_verify.verify(signature) # memunculkan cryptography.exceptions.InvalidSignature jika salahMetode .verify() dari library cryptography sangat berguna: ia memunculkan exception saat tidak cocok alih-alih mengembalikan boolean. Ini membuatnya lebih sulit untuk secara tidak sengaja mengabaikan kegagalan verifikasi. hmac.compare_digest() dari standard library mengembalikan True/ False, yang dapat diabaikan secara diam-diam jika developer lupa memeriksa nilai kembaliannya.
Output Terminal dengan Syntax Highlighting
Jika Anda melakukan debugging tanda tangan HMAC di terminal dan menginginkan output berwarna, library rich menangani ini dengan baik.
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 (32 karakter pertama)", 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)Bekerja dengan File Besar โ HMAC Bertahap
Untuk file di atas 50 MB, memuat semuanya ke memori hanya untuk menghitung HMAC adalah pemborosan. Metode .update() pada objek HMAC memungkinkan Anda memasukkan data dalam potongan. Ini menjaga penggunaan memori tetap konstan tanpa bergantung pada ukuran file.
import hmac
import hashlib
def hmac_file(key: bytes, filepath: str, chunk_size: int = 8192) -> str:
"""Hitung HMAC-SHA256 dari file tanpa memuatnya seluruhnya ke memori."""
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()
# Tandatangani ekspor database 2 GB
signing_key = b"backup_integrity_key_2026"
signature = hmac_file(signing_key, "/var/backups/db-export-2026-03.sql.gz")
print(f"HMAC-SHA256: {signature}")import hmac
import hashlib
def verify_file_hmac(key: bytes, filepath: str, expected_hex: str) -> bool:
"""Verifikasi tanda tangan HMAC-SHA256 dari sebuah file."""
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)
# Verifikasi artefak yang diunduh
is_valid = verify_file_hmac(
key=b"release_signing_key",
filepath="/tmp/release-v3.2.0.tar.gz",
expected_hex="8e3a1f9b2c4d6e7f0a1b3c5d7e9f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f",
)
print(f"Integritas file: {'valid' if is_valid else 'RUSAK'}").update() menggunakan memori chunk_size yang tetap terlepas dari ukuran file. Default 64 KB untuk potongan โ cukup besar untuk mengamortisasi overhead syscall, cukup kecil untuk tetap berada dalam cache L2 di sebagian besar perangkat keras.Membuat HMAC Key yang Aman secara Kriptografi di Python
Key yang lemah atau dapat diprediksi merusak seluruh konstruksi HMAC. Modul secrets (Python 3.6+) menyediakan byte acak yang kuat secara kriptografi. Untuk HMAC-SHA256, gunakan key 32 byte. Untuk HMAC-SHA512, gunakan 64 byte. Ukuran ini sesuai dengan ukuran blok internal algoritma hash masing-masing, yang merupakan panjang key optimal menurut RFC 2104.
import secrets
# Hasilkan key yang sesuai dengan ukuran blok algoritma hash
sha256_key = secrets.token_bytes(32) # 256 bits โ untuk HMAC-SHA256
sha512_key = secrets.token_bytes(64) # 512 bits โ untuk HMAC-SHA512
# Representasi hex โ aman untuk file konfigurasi dan environment variable
print(f"HMAC-SHA256 key: {sha256_key.hex()}")
# misalnya "a3f1b9c04e7d2f8a1b3c5d7e9f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f"
print(f"HMAC-SHA512 key: {sha512_key.hex()}")
# string hex 128 karakter
# URL-safe Base64 โ ringkas, aman untuk HTTP header
import base64
b64_key = base64.urlsafe_b64encode(sha256_key).decode("ascii")
print(f"Base64 key: {b64_key}")
# misalnya "o_G5wE59L4obPF1-nwortG2ODwobPExdXnqLnA0dLi8="random.random() atau random.randbytes() dari modul random untuk HMAC key. Modul random default menggunakan Mersenne Twister PRNG, yang dapat diprediksi setelah mengamati 624 output. Selalu gunakan secrets.token_bytes() untuk keacakan yang sensitif terhadap keamanan.Panjang Key dan Persyaratan RFC 2104
RFC 2104 menentukan bahwa HMAC key dapat memiliki panjang berapa pun, tetapi merekomendasikan key setidaknya L byte โ di mana L adalah panjang output dari fungsi hash yang mendasarinya. Untuk HMAC-SHA256, itu adalah 32 byte (256 bit). Key yang lebih pendek dari L bit mengurangi margin keamanan secara proporsional. Key yang lebih panjang dari ukuran blok hash (64 byte untuk SHA-256, 128 byte untuk SHA-512) terlebih dahulu di-hash ke ukuran blok, sehingga tidak ada manfaatnya menggunakan key yang lebih panjang dari ukuran blok. Tetap gunakan 32 byte untuk HMAC-SHA256 dan 64 byte untuk HMAC-SHA512.
Penyimpanan Key yang Aman dan Rotasi
Jangan pernah hardcode HMAC key dalam kode sumber. Pendekatan standar untuk deployment produksi adalah memuat key dari environment variable saat startup: os.environ["HMAC_SECRET"].encode(). Untuk lingkungan dengan keamanan lebih tinggi, simpan key dalam sistem manajemen secret seperti AWS Secrets Manager, HashiCorp Vault, atau GCP Secret Manager dan ambil saat runtime. Sistem-sistem ini menyediakan log audit, kontrol akses, dan rotasi otomatis tanpa memerlukan deployment kode baru.
Rencanakan rotasi key sejak awal. Ketika key dirotasi, ada jendela waktu di mana permintaan yang sedang berjalan ditandatangani dengan key lama dan akan gagal verifikasi terhadap key baru. Mitigasi standar adalah periode tumpang tindih singkat: terima tanda tangan dari key lama maupun baru untuk waktu singkat (menit hingga jam), kemudian pensiunkan key lama. Jika key dikompromikan โ terekspos di log, bocor melalui commit git, atau diungkapkan dalam insiden โ lakukan rotasi segera dan anggap semua tanda tangan yang dihasilkan dengan key yang dikompromikan sebagai tidak tepercaya. Verifikasi ulang hasil verifikasi yang di-cache dan beri tahu konsumen downstream tentang perubahan key.
Menggunakan bytearray dan memoryview dengan hmac.new()
Fungsi hmac.new() menerima objek bytes-like apa pun untuk parameter key dan msg. Ini berarti Anda dapat meneruskan bytes, bytearray, atau memoryview secara langsung, tanpa menyalin atau mengonversi. Ini paling penting dalam dua skenario: implementasi protokol jaringan di mana socket.recv_into() menulis data ke buffer bytearray yang sudah dialokasikan, dan sistem throughput tinggi di mana menghindari salinan perantara mengurangi tekanan GC. Slice memoryview adalah zero-copy: ia mengekspos jendela ke buffer asli tanpa mengalokasikan memori baru. Pada puluhan ribu pesan per detik, menghilangkan alokasi tersebut membuat perbedaan yang terukur dalam latensi dan throughput.
import hmac
import hashlib
# bytearray โ bytes yang dapat diubah, berguna untuk buffer protokol biner
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 โ slice zero-copy dari buffer yang lebih besar
large_buffer = bytearray(4096)
large_buffer[:20] = b"sensor_reading_12345"
# HMAC hanya 20 byte pertama tanpa menyalin
view = memoryview(large_buffer)[:20]
sig = hmac.new(key, view, hashlib.sha256).hexdigest()
print(f"Sensor signature: {sig[:32]}...")Kesalahan Umum
Dua kesalahan pertama hampir selalu muncul dalam setiap code review yang melibatkan webhook handler. Keduanya mudah terjadi di bawah tekanan waktu dan sulit terdeteksi tanpa mengetahui apa yang harus dicari.
Masalah: Operator == berhenti pada byte pertama yang tidak cocok, membocorkan informasi timing yang memungkinkan penyerang merekonstruksi tanda tangan yang diharapkan secara bertahap.
Solusi: Selalu gunakan hmac.compare_digest() untuk perbandingan tanda tangan โ ia berjalan dalam waktu konstan tanpa bergantung pada di mana ketidakcocokan terjadi.
received_sig = request.headers["X-Signature"]
expected_sig = hmac.new(key, body, hashlib.sha256).hexdigest()
if received_sig == expected_sig: # RENTAN terhadap timing attack
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): # constant-time
process_webhook(body)Masalah: hmac.new() membutuhkan objek bytes-like. Memasukkan str Python akan memunculkan TypeError: "key: expected bytes or bytearray, but got 'str'".
Solusi: Panggil .encode() pada string key dan pesan sebelum dikirim ke hmac.new().
key = "my_api_secret" # str, bukan bytes
msg = '{"event":"test"}' # str, bukan 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)Masalah: Memanggil hmac.new(key, msg) tanpa argumen ketiga akan memunculkan TypeError. Sebelum Python 3.4 default-nya adalah MD5, tetapi default tersebut dihapus karena alasan keamanan.
Solusi: Selalu berikan algoritma secara eksplisit: hashlib.sha256, hashlib.sha512, atau apa pun yang dibutuhkan protokol Anda.
# digestmod tidak ada โ memunculkan TypeError di Python 3.4+ sig = hmac.new(key, msg).hexdigest()
# Selalu tentukan algoritma hash sig = hmac.new(key, msg, hashlib.sha256).hexdigest()
Masalah: Banyak penyedia webhook (Stripe, Shopify) mengirimkan tanda tangan yang diencoding Base64, bukan hex. Membandingkan string hex dengan nilai Base64 selalu gagal, menyebabkan semua webhook ditolak.
Solusi: Periksa dokumentasi penyedia untuk format tanda tangan. Jika mereka menggunakan Base64, encode raw bytes dari .digest(), bukan string .hexdigest().
# Penyedia mengirim Base64, tetapi kita menghitung hex โ tidak pernah cocok expected = hmac.new(key, body, hashlib.sha256).hexdigest() # "a3f1b9c0..." vs "o/G5wE59..." โ selalu tidak cocok
import base64
# Sesuaikan format penyedia: raw bytes โ Base64
raw = hmac.new(key, body, hashlib.sha256).digest()
expected = base64.b64encode(raw).decode("ascii")
# "o/G5wE59..." โ cocok dengan headerstdlib hmac vs cryptography โ Perbandingan Singkat
Gunakan modul hmac dari stdlib untuk verifikasi webhook, penandatanganan API, dan operasi HMAC umum โ tidak membutuhkan dependensi apa pun dan mencakup setiap algoritma standar. Gunakan hmac.digest() untuk operasi batch di mana kecepatan one-shot penting. Gunakan library cryptography hanya ketika Anda sudah bergantung padanya untuk operasi lain (TLS, X.509, enkripsi simetris) dan menginginkan API .verify() berbasis exception. Untuk pemeriksaan tanda tangan cepat tanpa menulis kode Python apa pun, gunakan alat HMAC Generator untuk menempelkan key dan pesan Anda dan mendapatkan hasilnya secara instan.
Pertanyaan yang Sering Diajukan
Apa perbedaan antara hmac.new() dan hmac.digest() di Python?
hmac.new() mengembalikan objek HMAC yang mendukung pemanggilan .update() secara bertahap dan memberikan .digest() (raw bytes) maupun .hexdigest() (hex string). hmac.digest() adalah fungsi one-shot yang ditambahkan di Python 3.7 yang mengembalikan raw bytes secara langsung tanpa membuat objek perantara. Gunakan hmac.digest() ketika Anda memiliki pesan lengkap dan hanya membutuhkan hasilnya. Gunakan hmac.new() ketika Anda perlu memasukkan data secara bertahap atau membutuhkan output hex.
import hmac, hashlib
key = b"webhook_secret_2026"
body = b'{"event":"payment.completed","amount":9950}'
# One-shot โ mengembalikan raw bytes
raw = hmac.digest(key, body, hashlib.sha256)
# Object-based โ mendukung pembaruan bertahap dan output hex
h = hmac.new(key, body, hashlib.sha256)
hex_sig = h.hexdigest()Bagaimana cara memverifikasi tanda tangan HMAC di Python?
Hitung ulang HMAC dari pesan asli menggunakan secret yang sama, lalu bandingkan menggunakan hmac.compare_digest(). Jangan pernah gunakan == untuk perbandingan tanda tangan. Operator == rentan terhadap timing attack karena berhenti pada byte pertama yang tidak cocok, sehingga membocorkan informasi tentang panjang dan isi tanda tangan yang diharapkan.
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)Apakah hmac SHA-256 Python sama dengan hashing dengan hashlib.sha256?
Tidak. hashlib.sha256 menghitung hash SHA-256 biasa dari input, yang dapat dihitung oleh siapa saja. HMAC-SHA256 mencampurkan secret key ke dalam proses komputasi hash mengikuti RFC 2104, sehingga hanya seseorang dengan key tersebut yang dapat menghasilkan atau memverifikasi output yang benar. Hash biasa membuktikan integritas data. HMAC membuktikan integritas sekaligus keaslian.
import hmac, hashlib msg = b"transfer:9950:USD" key = b"api_secret_k8x2" plain_hash = hashlib.sha256(msg).hexdigest() # siapa saja bisa menghitung ini hmac_hash = hmac.new(key, msg, hashlib.sha256).hexdigest() # membutuhkan key
Bisakah saya menggunakan HMAC-SHA1 di Python 3?
Ya, berikan hashlib.sha1 sebagai argumen digestmod. HMAC-SHA1 masih berfungsi baik di Python 3 dan modul hmac tidak memiliki peringatan deprecation untuk itu. Meskipun demikian, SHA-1 dianggap lemah untuk desain baru โ ketahanan collision-nya di bawah 80 bit dan NIST telah men-deprecate-nya untuk sebagian besar penggunaan tanda tangan digital sejak 2015. Alasan utama menggunakan HMAC-SHA1 saat ini adalah kompatibilitas mundur dengan protokol yang membutuhkannya, seperti OAuth 1.0a atau sistem webhook lama tertentu. Ketika Anda mengontrol kedua sisi integrasi, lebih baik gunakan HMAC-SHA256 atau HMAC-SHA512 untuk semua pekerjaan baru.
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()
Bagaimana cara menghasilkan HMAC key yang aman di Python?
Gunakan secrets.token_bytes() dari standard library. Untuk HMAC-SHA256, key 32 byte adalah rekomendasi standar karena sesuai dengan ukuran blok hash. Untuk HMAC-SHA512, gunakan 64 byte. Jangan gunakan random.random() atau os.urandom() untuk pembuatan key dalam kode aplikasi โ secrets adalah modul yang tepat untuk keacakan yang sensitif terhadap keamanan sejak Python 3.6.
import secrets hmac_sha256_key = secrets.token_bytes(32) # 256 bits hmac_sha512_key = secrets.token_bytes(64) # 512 bits # Simpan sebagai hex untuk file konfigurasi print(hmac_sha256_key.hex()) # misalnya "a3f1b9c04e..."
Mengapa hmac.new() membutuhkan digestmod di Python 3?
Sebelum Python 3.4, digestmod default-nya adalah MD5, yang secara kriptografi sudah rusak โ MD5 memiliki serangan collision yang diketahui dan tidak boleh digunakan dalam kode baru yang sensitif terhadap keamanan. Para pengembang Python menghapus default tersebut untuk memaksa pilihan algoritma yang eksplisit, mencegah developer secara diam-diam mengirimkan MAC berbasis MD5. Jika Anda memanggil hmac.new(key, msg) tanpa digestmod, Anda akan mendapat TypeError. Selalu berikan algoritma secara eksplisit: hashlib.sha256, hashlib.sha512, atau konstruktor hashlib lainnya. Jika ragu, hashlib.sha256 adalah default yang aman โ tidak ada kelemahan yang diketahui dan cukup cepat untuk semua beban kerja praktis.
import hmac, hashlib key = b"secret" msg = b"data" # Ini akan memunculkan TypeError di Python 3.4+ # hmac.new(key, msg) # TypeError: missing required argument: 'digestmod' # Selalu tentukan algoritmanya h = hmac.new(key, msg, hashlib.sha256)
Untuk pemeriksaan HMAC cepat tanpa menjalankan skrip Python, tempelkan key dan pesan Anda ke HMAC Generator online โ mendukung SHA-256, SHA-384, dan SHA-512 dengan hasil instan.
Alat Terkait
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.