HMAC Explained
HMAC (Hash-Based Message Authentication Code) is a mechanism for verifying both the integrity and authenticity of a message using a cryptographic hash function combined with a secret key. Unlike plain hashing, which anyone can compute, HMAC requires knowledge of the secret key to generate or verify the authentication code.
A plain hash like SHA256 tells you that a message has not changed, but it does not prove who created it. An attacker who modifies the message can simply recompute the hash, and the recipient has no way to detect the tampering. HMAC solves this by incorporating a secret key into the hash computation. Only someone with the key can produce a valid HMAC, making it suitable for authenticating messages between parties that share a secret.
How HMAC Works
HMAC uses two passes of the hash function with the secret key. The algorithm is defined in RFC 2104 and works as follows:
- The secret key is padded to the hash function's block size
- The padded key is XORed with an inner padding constant (ipad = 0x36 repeated)
- The message is appended to the inner padded key
- The result is hashed
- The padded key is XORed with an outer padding constant (opad = 0x5C repeated)
- The hash from step 4 is appended to the outer padded key
- The result is hashed again
HMAC(K, m) = H((K' ⊕ opad) || H((K' ⊕ ipad) || m))
This double-hashing approach prevents length extension attacks and ensures that even if the underlying hash function has weaknesses, the HMAC construction remains secure.
HMAC vs Plain Hashing
| Feature | Plain Hash (SHA256) | HMAC-SHA256 |
|---|---|---|
| Key required | No | Yes |
| Integrity check | Yes | Yes |
| Authentication | No | Yes |
| Tamper detection | Full recompute | Detection without key |
| Best for | File checksums | API authentication |
| Length extension | Vulnerable | Resistant |
Common Use Cases
API authentication — the client signs each request with HMAC using a shared secret. The server recomputes the HMAC and compares it. If they match, the request is authentic.
JWT token signing — although JWT typically uses simpler HMAC or RSA signing, HMAC is a valid algorithm for JWT (HS256, HS384, HS512).
Webhook verification — services like Stripe, GitHub, and Slack sign webhook payloads with HMAC so you can verify they came from the service.
Session tokens — HMAC can generate unforgeable session tokens without storing them in a database.
Code Examples
PHP
$secret = 'my-secret-key';
$message = 'message-to-authenticate';
// Generate HMAC
$hmac = hash_hmac('sha256', $message, $secret);
echo $hmac;
// Verify HMAC
$expected = hash_hmac('sha256', $message, $secret);
if (hash_equals($expected, $providedHmac)) {
echo 'Authentic';
}
Python
import hmac
import hashlib
secret = b'my-secret-key'
message = b'message-to-authenticate'
# Generate HMAC
hmac_value = hmac.new(secret, message, hashlib.sha256).hexdigest()
print(hmac_value)
# Verify HMAC (constant-time comparison)
expected = hmac.new(secret, message, hashlib.sha256).digest()
received = bytes.fromhex(provided_hmac)
if hmac.compare_digest(expected, received):
print('Authentic')
JavaScript (Node.js)
const crypto = require('crypto');
const secret = 'my-secret-key';
const message = 'message-to-authenticate';
// Generate HMAC
const hmac = crypto.createHmac('sha256', secret)
.update(message)
.digest('hex');
console.log(hmac);
// Verify
const expected = crypto.createHmac('sha256', secret)
.update(message)
.digest();
HMAC Algorithms
HMAC can use any cryptographic hash function. The algorithm name typically combines "HMAC" with the hash name.
| Algorithm | Hash Function | Output Size |
|---|---|---|
| HMAC-MD5 | MD5 | 128 bits |
| HMAC-SHA1 | SHA-1 | 160 bits |
| HMAC-SHA256 | SHA-256 | 256 bits |
| HMAC-SHA512 | SHA-512 | 512 bits |
| HMAC-SHA3-256 | SHA3-256 | 256 bits |
Use HMAC-SHA256 as the default. HMAC-MD5 and HMAC-SHA1 are deprecated for security-sensitive applications.
Security Considerations
Key size — the key should be at least as long as the hash output. For HMAC-SHA256, use a key of at least 32 bytes (256 bits). Longer keys are hashed down to the block size.
Key secrecy — the security of HMAC depends entirely on the key being secret. Use a key management system or environment variables. Never hardcode keys in source code.
Constant-time comparison — always compare HMAC values using a constant-time comparison function like hash_equals() in PHP or hmac.compare_digest() in Python. A timing attack could otherwise reveal the correct HMAC byte by byte.
Replay attacks — HMAC alone does not prevent replay attacks. Include a timestamp or nonce in the signed message and verify it on the server.
Online Tool
The HMAC Generator on Help2Code computes HMAC values with your choice of algorithm and secret key. Use it to test HMAC implementations or generate test vectors during development.
Conclusion
HMAC provides message authentication using a shared secret and a cryptographic hash function. It is essential for API security, webhook verification, and any scenario where you need to verify both the integrity and authenticity of a message. Use HMAC-SHA256 with a strong secret key and always compare HMAC values using constant-time functions.