What Is HMAC? A Practical Guide to HMAC-SHA256

What Is HMAC? A Practical Guide to HMAC-SHA256

Try the Hash Generator

HMAC (Hash-based Message Authentication Code) is everywhere in modern software, even if you have never called it by name. Every time AWS authenticates an API request, Stripe sends a webhook, or a service issues a signed JWT, HMAC is doing the work behind the scenes. This guide explains what HMAC is, how it differs from a plain hash, how HMAC-SHA256 works step by step, and how to generate and verify HMACs in real code.

You can experiment with HMAC right now using the Hash Generator: enable HMAC mode, paste a secret key, and watch the digest change as you edit the message.

HMAC in One Sentence

HMAC is a way to prove two things at once: that a message has not been altered (integrity) and that it came from someone who knows a shared secret key (authenticity). A plain hash like SHA-256 only proves integrity — anyone can recompute it. HMAC adds a secret key, so only parties who hold that key can produce or verify a valid code.

Why a Plain Hash Is Not Enough

Suppose you send a message and its SHA-256 hash to a server so it can detect tampering:

message = "amount=100&to=alice"
digest  = SHA256(message)

The problem: an attacker who intercepts the request can change the message and recompute the hash, because SHA-256 is public and keyless. The server has no way to tell the forged pair apart from the real one.

HMAC fixes this by mixing in a secret key that the attacker does not have:

digest = HMAC-SHA256(key, message)

Now, without the key, an attacker cannot generate a digest that the server will accept. Changing the message invalidates the code.

How HMAC Works

HMAC is defined in RFC 2104 and works with any cryptographic hash function — HMAC-SHA256, HMAC-SHA512, HMAC-SHA1, and so on. The construction is deliberately simple but carefully designed:

HMAC(key, message) = H( (key ⊕ opad) || H( (key ⊕ ipad) || message ) )

Breaking that down:

  1. The key is padded (or hashed first, if it is longer than the block size) to the hash's block length.
  2. Two constants are XORed with the key: the inner pad ipad (the byte 0x36 repeated) and the outer pad opad (the byte 0x5c repeated).
  3. The message is hashed together with the inner-padded key.
  4. That result is hashed again together with the outer-padded key.

The double-hashing with two different key-derived values is what protects HMAC against length-extension attacks, which affect a naive H(key || message) design built on SHA-256 or MD5.

A Worked Example

With the message Hello, World! and the key secret, HMAC-SHA256 produces:

HMAC-SHA256("secret", "Hello, World!")
  = fb6d8d7efc4a3b... (a 64-character hex string)

The output is always the same length as the underlying hash — 256 bits (64 hex characters) for SHA-256, regardless of how long the message is. Try it yourself in the Hash Generator with HMAC mode enabled.

HMAC-SHA256 vs Other Variants

The hash function inside HMAC determines its output size and security margin:

Variant Output Common Uses
HMAC-MD5 128 bits Legacy systems only — avoid for new designs
HMAC-SHA1 160 bits Older TLS, some webhooks (being phased out)
HMAC-SHA256 256 bits AWS, Stripe, GitHub, JWT — the modern default
HMAC-SHA512 512 bits BIP-32 wallets, high-security key derivation

Interestingly, HMAC is more resilient than its underlying hash. Even HMAC-MD5 has no practical break, because HMAC does not rely on collision resistance the way a bare hash does. Still, HMAC-SHA256 is the right default for new systems — it is universally supported and future-proof. For a deeper look at the trade-offs between the two SHA-2 sizes, see SHA-256 vs SHA-512.

Real-World Uses of HMAC

Signing API Requests

AWS Signature Version 4 derives a signing key through a chain of HMAC-SHA256 operations and signs every request with it. The server repeats the calculation; if the signatures match, the request is authentic and untampered.

Verifying Webhooks

When Stripe, GitHub, or Shopify send you a webhook, they include an HMAC signature header computed over the raw request body using a secret you both share. Your endpoint must recompute the HMAC and compare:

import hmac, hashlib

def verify(secret: bytes, body: bytes, signature: str) -> bool:
    expected = hmac.new(secret, body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)

Using hmac.compare_digest (a constant-time comparison) is essential — a naive == can leak the correct signature through timing differences.

Stateless Sessions and JWTs

A JSON Web Token signed with HS256 is just HMAC-SHA256(secret, header.payload). The server can verify the token without a database lookup, because only it holds the secret needed to reproduce the signature.

Generating HMAC in Code

Node.js:

const crypto = require('crypto');
const sig = crypto
  .createHmac('sha256', 'secret')
  .update('Hello, World!')
  .digest('hex');

Python:

import hmac, hashlib
sig = hmac.new(b'secret', b'Hello, World!', hashlib.sha256).hexdigest()

PHP:

$sig = hash_hmac('sha256', 'Hello, World!', 'secret');

All three produce the identical 64-character hex string — and so does the Hash Generator with HMAC mode on, which is handy for debugging when a signature mismatch is driving you up the wall.

Common Mistakes

  • Comparing signatures with ==. Always use a constant-time comparison (hmac.compare_digest, crypto.timingSafeEqual, or PHP's hash_equals) to avoid timing attacks.
  • Hashing the parsed body instead of the raw bytes. Webhook signatures are computed over the exact raw payload. Re-serializing JSON can change whitespace and break verification.
  • Reusing the same secret everywhere. Use distinct keys per integration so a single leak has limited blast radius.
  • Treating HMAC as encryption. HMAC authenticates a message; it does not hide it. To learn the difference between authentication, hashing, and encryption, read hashing vs encryption.

FAQ

What does HMAC stand for?

HMAC stands for Hash-based Message Authentication Code. It combines a cryptographic hash function (such as SHA-256) with a secret key to produce a code that verifies both the integrity and the authenticity of a message.

What is the difference between a hash and an HMAC?

A plain hash like SHA-256 uses no key, so anyone can compute it — it only proves data has not changed. An HMAC mixes in a secret key, so only parties holding that key can generate or verify a valid code. HMAC therefore proves both integrity and authenticity.

Is HMAC-SHA256 secure?

Yes. HMAC-SHA256 has no known practical attacks and is the recommended default for API signing, webhooks, and token authentication. Its security depends mainly on keeping the secret key private and using a long, random key.

Can HMAC be reversed or decrypted?

No. HMAC is built on a one-way hash function, so there is no way to recover the original message or the key from the code. It is used for verification, not for hiding data. If you need confidentiality, use encryption alongside HMAC.

How long should an HMAC secret key be?

Use a random key at least as long as the hash output — 32 bytes (256 bits) for HMAC-SHA256 is a good baseline. Longer keys do not hurt, but shorter or low-entropy keys (like dictionary words) weaken the whole scheme.

Why does my webhook signature check keep failing?

The most common cause is hashing the re-serialized body instead of the raw request bytes. Compute the HMAC over the exact payload you received, ensure you are using the correct secret, and confirm the algorithm (usually SHA-256) and encoding (hex vs base64) match what the provider documents.


Related Tools:

Generate Hashes Instantly

Create MD5, SHA-256, SHA-512, bcrypt, and more — 100% client-side, your data never leaves your browser.

Open Hash Generator