Cryptography / Message Authentication

HMAC-SHA256

Hash-based Message Authentication Code · Interactive Step-by-Step Visualization
01 / What is HMAC

A keyed fingerprint for your data

HMAC (Hash-based Message Authentication Code) is a construction that turns any cryptographic hash function into a message authenticator. It takes two inputs — a secret key and a message — and produces a fixed-size tag that proves both who created the message and that it hasn't been tampered with.

Unlike a plain hash (which anyone can compute), an HMAC can only be verified by someone who holds the secret key. This makes it the standard tool for API authentication, signed tokens, and webhook verification.

Full formula
HMAC(K, m) = H( (K′ ⊕ opad)H( (K′ ⊕ ipad)m ) )
K′ = key padded to block size ipad = 0x36 × 64 opad = 0x5C × 64 H = SHA-256
How it compares
Plain HashHMACDigital Sig
Needs secret keyNoYes (shared)Yes (private)
Verifiable by anyoneYesOnly key holdersYes (public key)
Tamper detectionNoYesYes
Non-repudiationNoNoYes
Length-extension safeNoYesYes
SpeedFastFastSlow
02 / When it's used

Real-world use cases

JWT HS256
JSON Web Tokens signed with HMAC-SHA256. The server signs the header+payload with a secret; any tampering invalidates the signature.
Header.Payload → HMAC → Signature
AWS Signature V4
Every AWS API request is signed using a chain of HMAC-SHA256 operations over the date, region, service, and request body.
HMAC(HMAC(HMAC(HMAC(key, date), region), service), request)
Webhook Verification
Stripe, GitHub, Shopify all send an HMAC of the payload in the request header. Your server recomputes and compares to confirm origin.
X-Stripe-Signature: HMAC(secret, timestamp.body)
TOTP / HOTP
One-time passwords (RFC 4226 / 6238) are derived from HMAC-SHA1 of a shared secret and a counter or timestamp — the basis of every authenticator app.
OTP = Truncate(HMAC(secret, counter))
TLS 1.2 PRF
TLS 1.2 uses HMAC-SHA256 inside its Pseudorandom Function to derive session keys from the master secret and random nonces.
PRF(secret, label, seed) = P_SHA256(secret, label ∥ seed)
Cookie / Session Signing
Frameworks like Express and Django sign session cookies with HMAC so the server can detect if a client has tampered with the cookie value.
cookie = value + "." + HMAC(secret, value)
03 / How it's used in code

Implementation in 3 languages

import { createHmac } from 'crypto';

// Sign a message
const key     = 'my-secret-key';
const message = 'Hello, HMAC!';

const hmac = createHmac('sha256', key)
  .update(message)
  .digest('hex');

console.log(hmac);
// → 64-char hex string

// Verify (timing-safe comparison)
import { timingSafeEqual } from 'crypto';

function verify(key, message, expected) {
  const actual = createHmac('sha256', key)
    .update(message).digest();
  return timingSafeEqual(actual, Buffer.from(expected, 'hex'));
}
import hmac, hashlib

key     = b'my-secret-key'
message = b'Hello, HMAC!'

# Sign a message
tag = hmac.new(key, message, hashlib.sha256).hexdigest()
print(tag)
# → 64-char hex string

# Verify (timing-safe comparison)
def verify(key, message, expected_hex):
    actual = hmac.new(key, message, hashlib.sha256).digest()
    expected = bytes.fromhex(expected_hex)
    # hmac.compare_digest prevents timing attacks
    return hmac.compare_digest(actual, expected)
import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
)

// Sign a message
func Sign(key, message []byte) string {
    mac := hmac.New(sha256.New, key)
    mac.Write(message)
    return hex.EncodeToString(mac.Sum(nil))
}

// Verify (timing-safe)
func Verify(key, message []byte, expected string) bool {
    tag, _ := hex.DecodeString(expected)
    mac := hmac.New(sha256.New, key)
    mac.Write(message)
    actual := mac.Sum(nil)
    // hmac.Equal uses constant-time comparison
    return hmac.Equal(actual, tag)
}
Critical security notes
Always use timing-safe comparison

Never compare HMAC tags with ==. A naive string comparison short-circuits on the first mismatched byte, leaking timing information that can be used to forge tags byte-by-byte.

Key must be secret and random

The security of HMAC is entirely in the key. Use a cryptographically random key of at least 32 bytes. Never reuse the same key for different purposes (e.g. signing tokens vs verifying webhooks).

HMAC is not encryption

HMAC proves integrity and authenticity but does not hide the message content. The message is visible to anyone who receives it — use HMAC alongside encryption (e.g. AES-GCM) if confidentiality is needed.

Include a timestamp or nonce

HMAC alone doesn't prevent replay attacks. In production (webhooks, APIs) always include a timestamp in the signed payload and reject messages older than a few minutes.

// Interactive Demo — step through the algorithm with your own values
Step 1 / 7
← → keys
Enter a key and message, then click Compute HMAC to visualize every step of the algorithm with real values.