Hash-based Message Authentication Code (HMAC)
HMAC is a cryptographic construction combining a hash function with a secret key to verify data integrity and message authenticity per RFC 2104.
A Hash-based Message Authentication Code (HMAC) is a cryptographic algorithm defined in RFC 2104 that combines a cryptographic hash function (such as SHA-256 or SHA-512) with a shared secret key. It provides two vital security guarantees simultaneously: data integrity (verifying that the message has not been altered or tampered with in transit) and data authenticity (proving that the sender possesses the shared secret key).
You can generate and verify cryptographic hashes and HMAC digests using our client-side Hash Generator tool.
Technical Specifications at a Glance
| Specification | HMAC-SHA256 | HMAC-SHA512 | HMAC-MD5 (Legacy) |
|---|---|---|---|
| Standard Reference | RFC 2104 / FIPS PUB 198-1 | RFC 2104 / FIPS PUB 198-1 | RFC 2104 (Deprecated) |
| Underlying Hash Function | SHA-256 | SHA-512 | MD5 |
| Output Digest Size | 256 bits (32 bytes) | 512 bits (64 bytes) | 128 bits (16 bytes) |
| Hash Block Size ($B$) | 64 bytes (512 bits) | 128 bytes (1024 bits) | 64 bytes (512 bits) |
| Recommended Key Length | $\ge$ 32 bytes (256 bits) | $\ge$ 64 bytes (512 bits) | Obsolete / Insecure |
| Cryptographic Model | Symmetric (Shared Secret) | Symmetric (Shared Secret) | Symmetric (Shared Secret) |
| Resistance to Length Extension | Immune | Immune | Immune (but MD5 is broken) |
How HMAC Works: The Mathematical Construction
A naive attempt to create a keyed hash might simply concatenate the secret key with the message: Hash(Secret + Message). However, Merkle–Damgård hash functions (including MD5, SHA-1, and SHA-256) are vulnerable to length extension attacks, where an attacker who observes Hash(Secret + Message) can append arbitrary data to the payload and compute a valid signature without ever knowing the secret key!
HMAC completely immunizes against length extension attacks through a two-pass nested hashing construction:
$$\text{HMAC}(K, m) = H\Big((K' \oplus \text{opad}) \mathbin{\Vert} H\big((K' \oplus \text{ipad}) \mathbin{\Vert} m\big)\Big)$$
Where:
- $H$ is the underlying cryptographic hash function (e.g., SHA-256).
- $K$ is the secret key. If $K$ is longer than the block size $B$, it is hashed ($K' = H(K)$); if shorter, it is padded with zeros up to $B$ bytes.
- $m$ is the message to be authenticated.
- $\text{ipad}$ (inner padding) is the byte
0x36repeated $B$ times. - $\text{opad}$ (outer padding) is the byte
0x5Crepeated $B$ times. - $\oplus$ denotes bitwise exclusive-or (XOR).
- $\Vert$ denotes byte concatenation.
Step 1: Key (K') XOR ipad ───────────┐
▼
Step 2: [ (K' ⊕ ipad) + Message ] ──► Inner Hash ──┐
▼
Step 3: Key (K') XOR opad ──────────► [ (K' ⊕ opad) + Inner Hash ] ──► Outer Hash (Final HMAC)
Real-World Applications
- Webhook Signature Verification: Third-party payment gateways and SaaS platforms (e.g., Stripe, GitHub, Shopify) compute an HMAC-SHA256 of every webhook payload using your webhook secret and send it in an HTTP header (e.g.,
Stripe-Signature). Your server recalculates the HMAC to guarantee the webhook originated from Stripe and was not intercepted or forged. - JSON Web Tokens (JWT HS256): The
HS256signing algorithm in the JWT specification is HMAC with SHA-256, verifying the claims token across distributed microservices. - API Request Signing (AWS SigV4): Amazon Web Services and cloud providers require every REST API request to be signed with a hierarchical HMAC-SHA256 signature combining your secret access key, timestamps, HTTP headers, and request body.
Code Example: Generating & Verifying HMAC
Node.js (with Constant-Time Timing Attack Protection)
import crypto from 'node:crypto';
// 1. Generate an HMAC-SHA256 signature
export function signPayload(message, secretKey) {
return crypto
.createHmac('sha256', secretKey)
.update(message, 'utf8')
.digest('hex');
}
// 2. Safely verify an incoming signature (timing-safe)
export function verifySignature(message, incomingSignature, secretKey) {
const expectedSignature = signPayload(message, secretKey);
const expectedBuffer = Buffer.from(expectedSignature, 'hex');
const incomingBuffer = Buffer.from(incomingSignature, 'hex');
// Prevent length-based timing leaks
if (expectedBuffer.length !== incomingBuffer.length) {
return false;
}
// Cryptographically safe constant-time comparison
return crypto.timingSafeEqual(expectedBuffer, incomingBuffer);
}
Python 3
import hmac
import hashlib
def generate_hmac(message: str, secret_key: str) -> str:
key_bytes = secret_key.encode('utf-8')
msg_bytes = message.encode('utf-8')
return hmac.new(key_bytes, msg_bytes, hashlib.sha256).hexdigest()
def verify_hmac(message: str, signature: str, secret_key: str) -> bool:
expected = generate_hmac(message, secret_key)
# hmac.compare_digest prevents timing attacks
return hmac.compare_digest(expected, signature)
Security Best Practices & Gotchas
- Always Use Constant-Time String Comparison: Standard string comparison operators (
===in JavaScript or==in Python) terminate execution at the first non-matching character. Attackers can measure response latencies down to nanoseconds to deduce valid signature bytes one by one (a timing attack). Always usecrypto.timingSafeEqual()in Node.js orhmac.compare_digest()in Python. - Enforce Adequate Key Entropy: An HMAC is only as secure as its secret key. Generating weak, guessable keys allows adversaries to mount offline brute-force attacks. Use a cryptographically secure random generator to create keys with at least 256 bits of entropy.
- HMAC is Symmetric, Not Asymmetric: Both sender and receiver must know the shared secret. If you need public verification without distributing the private signing key, use asymmetric digital signatures such as ECDSA or Ed25519 instead.
Frequently Asked Questions
What is the difference between HMAC and a standard hash like SHA-256?
A standard hash like SHA-256 takes only data as input and produces a fixed fingerprint; anyone can compute the same hash. An HMAC requires both data and a secret cryptographic key. Without knowing the secret key, an attacker cannot generate a valid HMAC or forge messages.
Why not just compute hash(secret + message)?
Because Merkle–Damgård hash functions (like MD5 and SHA-256) process data in sequential blocks, an attacker who intercepts hash(secret + message) can leverage length extension attacks to append new data to the payload and compute the resulting hash without knowing the secret. HMAC was specifically designed to prevent this flaw.
Can an HMAC be decrypted to reveal the original message?
No. HMAC relies on one-way cryptographic hash functions. It is mathematically impossible to reverse or decrypt an HMAC to recover either the message or the secret key. If you need two-way reversible encryption, use a symmetric cipher like AES.
Which hash function should I pair with HMAC?
HMAC-SHA256 is the industry standard for webhooks, REST APIs, and microservices. For high-security environments, HMAC-SHA512 is recommended. Avoid legacy HMAC-MD5 or HMAC-SHA1, as their underlying hash algorithms are cryptographically compromised.
Interactive Tools for Hash-based Message Authentication Code (HMAC)
Free, browser-based utilities to test, generate, and inspect Hash-based Message Authentication Code (HMAC) payloads directly.