HMAC Python โ€” Panduan hmac.new() SHA-256 + Contoh Kode

ยทDevOps Engineer & Python Automation SpecialistยทDitinjau olehMaria SantosยทDiterbitkan

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.

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

Python 3.7+ โ€” contoh HMAC-SHA256 minimal
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.

Python 3.7+ โ€” .digest() vs .hexdigest()
import hmac
import hashlib

key = b"service_auth_key"
msg = b"GET /api/v2/orders 2026-03-28T14:30:00Z"

h = hmac.new(key, msg, hashlib.sha256)

raw_bytes = h.digest()
print(type(raw_bytes), len(raw_bytes))
# <class 'bytes'> 32

hex_string = h.hexdigest()
print(type(hex_string), len(hex_string))
# <class 'str'> 64

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

Python 3.7+ โ€” mengonversi string ke bytes
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 konsisten
Catatan:Parameter digestmod 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().

Python 3.7+ โ€” encoding Base64 HMAC-SHA256
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.

Python 3.7+ โ€” HMAC-SHA1
import hmac
import hashlib

consumer_secret = b"oauth_consumer_secret_2026"
token_secret = b"oauth_token_secret_2026"

# OAuth 1.0a 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 header

HMAC-SHA512 โ€” Output Lebih Panjang

Python 3.7+ โ€” HMAC-SHA512
import hmac
import hashlib

key = b"high_security_signing_key_64_bytes_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
msg = b'{"transfer_id":"xfr_9c2e","amount":500000,"currency":"EUR"}'

h = hmac.new(key, msg, hashlib.sha512)

print(len(h.digest()))   # 64 bytes (512 bits)
print(len(h.hexdigest()))  # 128 hex characters
print(h.hexdigest()[:40] + "...")
# "8e3a1f9b2c4d6e7f0a1b3c5d7e9f0a2b4c6d8e0f..."

HMAC-MD5 โ€” Hanya untuk Kompatibilitas

Python 3.7+ โ€” HMAC-MD5
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"
Peringatan:HMAC-MD5 hanya dapat diterima untuk kompatibilitas mundur dengan sistem yang tidak dapat Anda migrasi. Untuk proyek baru apa pun, gunakan minimal HMAC-SHA256. MD5 memiliki kerentanan collision yang diketahui yang, meskipun kurang dapat dieksploitasi langsung dalam mode HMAC, menjadikannya pilihan default yang buruk.

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

Parameter
Tipe
Default
Deskripsi
key
bytes | bytearray
(wajib)
Secret key โ€” harus berupa bytes, bukan string
msg
bytes | None
None
Pesan awal untuk di-hash; data tambahan dapat ditambahkan via .update()
digestmod
str | callable
(wajib)
Algoritma hash โ€” misalnya hashlib.sha256 atau string "sha256"

hmac.digest() one-shot (Python 3.7+)

Parameter
Tipe
Deskripsi
key
bytes
Secret key
msg
bytes
Pesan yang akan diautentikasi
digest
str | callable
Algoritma hash โ€” sama dengan digestmod pada hmac.new()

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.

Python 3.7+ โ€” hmac.digest() one-shot
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.

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

Python 3.7+ โ€” verifikasi HMAC webhook (Flask)
import hmac
import hashlib
from flask import Flask, request, abort

app = Flask(__name__)
WEBHOOK_SECRET = b"whsec_MbkP7x9yFqHGn3tRdWz5"

@app.route("/webhooks/payments", methods=["POST"])
def handle_payment_webhook():
    # 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 "", 200

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

Python 3.7+ โ€” verifikasi X-Hub-Signature-256 GitHub
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 "", 200

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

Python 3.7+ โ€” penandatanganan permintaan API dengan HMAC-SHA256
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

Python 3.7+ โ€” permintaan bertanda tangan HMAC 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.

bash โ€” HMAC-SHA256 via one-liner Python
python3 -c "
import hmac, hashlib
print(hmac.new(b'my_secret', b'message_to_sign', hashlib.sha256).hexdigest())
"
# outputs: 64-character hex string
bash โ€” HMAC-SHA256 via openssl (untuk perbandingan)
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
bash โ€” HMAC dari environment variable key
# 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())
"
Catatan:Flag 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.

bash โ€” install cryptography
pip install cryptography
Python 3.7+ โ€” HMAC via library 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 salah

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

bash โ€” install rich
pip install rich
Python 3.7+ โ€” output HMAC berwarna dengan 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)
Catatan:Rich hanya untuk tampilan terminal. Jangan gunakan saat menulis tanda tangan HMAC ke file, HTTP header, atau sistem agregasi log โ€” kode escape ANSI akan merusak outputnya.

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.

Python 3.7+ โ€” HMAC file besar dalam potongan
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}")
Python 3.7+ โ€” verifikasi tanda tangan HMAC file yang diunduh
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'}")
Catatan:Beralih ke HMAC bertahap ketika file melebihi 50-100 MB atau saat memproses upload di web server di mana memori per permintaan penting. Pendekatan .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.

Python 3.7+ โ€” buat HMAC key dengan secrets
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="
Peringatan:Jangan pernah gunakan 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.

Python 3.7+ โ€” bytearray dan memoryview dengan HMAC
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.

โŒ Membandingkan tanda tangan HMAC dengan == alih-alih hmac.compare_digest()

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.

Before ยท Python
After ยท Python
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)
โŒ Memasukkan string alih-alih bytes ke hmac.new()

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

Before ยท Python
After ยท Python
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)
โŒ Lupa digestmod (Python 3.4+)

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.

Before ยท Python
After ยท Python
# 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()
โŒ Menggunakan .hexdigest() ketika penyedia mengharapkan Base64

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

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

stdlib hmac vs cryptography โ€” Perbandingan Singkat

Method
Algoritma
Output
Streaming
Tipe Kustom
Perlu Install
hmac.new() + hexdigest()
Any hashlib
Hex string
โœ“ via .update()
N/A
Tidak (stdlib)
hmac.new() + digest()
Any hashlib
Raw bytes
โœ“ via .update()
N/A
Tidak (stdlib)
hmac.digest()
Any hashlib
Raw bytes
โœ— (one-shot)
N/A
Tidak (stdlib, 3.7+)
hashlib.sha256 (plain hash)
SHA-256 only
Hex atau bytes
โœ“ via .update()
N/A
Tidak (stdlib)
cryptography (HMAC)
Any
Raw bytes
โœ“ via .update()
N/A
pip install
pyca/cryptography + CMAC
AES-CMAC
Raw bytes
โœ“ via .update()
N/A
pip install

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.

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

Python
import hmac, hashlib

def verify_signature(secret: bytes, message: bytes, received_sig: str) -> bool:
    expected = hmac.new(secret, message, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, received_sig)

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.

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

Python
import hmac, hashlib

key = b"oauth_consumer_secret"
base_string = b"GET&https%3A%2F%2Fapi.example.com&oauth_nonce%3Dabc123"
sig = hmac.new(key, base_string, hashlib.sha1).digest()

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.

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

Python
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

DV
Dmitri VolkovDevOps Engineer & Python Automation Specialist

Dmitri is a DevOps engineer who relies on Python as his primary scripting and automation language. He builds internal tooling, CI/CD pipelines, and infrastructure automation scripts that run in production across distributed teams. He writes about the Python standard library, subprocess management, file processing, encoding utilities, and the practical shell-adjacent Python that DevOps engineers use every day.

MS
Maria SantosPeninjau teknis

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.