HMAC (Hash-based Message Authentication Code) ใช้ยืนยัน Integrity และ Authenticity ของข้อความ ด้วย Secret Key และ Hash Function (MD5, SHA-1, SHA-256, etc.)
import hmac, import hashlib
หลักการทำงาน
HMAC(K, m) = H((K' ⊕ opad) || H((K' ⊕ ipad) || m))
K= Secret Keym= MessageH= Hash Function- Key ถูก Pad/Truncate ให้เท่า Block Size
เมธอดหลักของ HMAC Object
hmac.new(key, msg=None, digestmod='')- สร้าง HMAC Objectupdate(data)- เพิ่มข้อมูล (เรียกซ้ำได้ = Concatenate)digest()- คืน Binary Digesthexdigest()- คืน Hex String (สะดวกส่งผ่าน Network)copy()- คัดลอก Object (คำนวณต่อจากจุดเดิมได้)
โค้ดตัวอย่าง
import hmac
import hashlib
# ข้อมูลและ Key
message = b'Python123'
key = b'abcde1234'
# สร้าง HMAC-SHA256
h = hmac.new(key, message, hashlib.sha256)
print("Digest (bytes):", h.digest())
print("Hex Digest:", h.hexdigest())
print("Algorithm:", h.name) # hmac-sha256
# Copy และคำนวณต่อ
h2 = h.copy()
h2.update(b' more data')
print("Extended:", h2.hexdigest())
# Verify HMAC (Constant-time Comparison)
received_mac = b'...'
expected_mac = h.digest()
if hmac.compare_digest(received_mac, expected_mac):
print("Valid!")
else:
print("Invalid!")
สำคัญ: hmac.compare_digest(a, b)
เปรียบเทียบแบบ Constant-Time ป้องกัน Timing Attack
# ❌ ไม่ปลอดภัย if h.hexdigest() == received_hex: ... # ✅ ปลอดภัย if hmac.compare_digest(h.digest(), received_bytes): ...
กรณีใช้งานจริง
- API Authentication: Sign Request Parameters
- Webhook Verification: GitHub, Stripe, Slack ส่ง HMAC Signature
- Cookie Signing: ป้องกัน Tampering
- Password Reset Tokens: HMAC(user_id + timestamp, secret)
- JWT Alternative: Simple Signed Payload
ตัวอย่าง: Verify Webhook Signature (GitHub Style)
import hmac, hashlib
def verify_webhook(payload, signature_header, secret):
# signature_header = "sha256=abc123..."
algo, received_sig = signature_header.split('=', 1)
expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, received_sig)
# Usage
if verify_webhook(request.body, request.headers['X-Hub-Signature-256'], WEBHOOK_SECRET):
process_webhook()
else:
abort(401, "Invalid signature")