← All Posts

How to Generate HMAC Signatures (SHA-256, SHA-512)

Published on February 10, 2026

What Is HMAC?

HMAC stands for Hash-based Message Authentication Code. It is a mechanism that combines a cryptographic hash function (like SHA-256 or SHA-512) with a secret key to produce a fixed-size signature for a given message. The result is called a message authentication code (MAC) and serves two purposes: it verifies the integrity of the message (has it been tampered with?) and authenticates the sender (does the sender possess the secret key?).

HMAC was defined in RFC 2104 and has become the standard mechanism for API authentication, webhook verification, and message integrity in modern web applications. Services like Stripe, GitHub, Shopify, and Twilio all use HMAC signatures to secure their webhooks.

HMAC vs Plain Hash

A plain hash function like SHA-256 takes only a message as input and produces a hash. Anyone who knows the hash algorithm can compute the same hash for the same message. This means a plain hash can verify integrity (the message was not altered) but cannot verify authenticity (who sent the message).

HMAC adds a secret key to the equation. Only parties that possess the secret key can generate or verify the HMAC. This means an attacker who intercepts the message cannot forge a valid HMAC without knowing the key.

// Plain hash — no secret, anyone can compute
SHA-256("Hello World")
→ a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e

// HMAC — requires the secret key to compute
HMAC-SHA256("my-secret-key", "Hello World")
→ a4b93f2b6c8e3d1f5a9e7c2d8b4f6e0a1c3d5e7f9b2d4f6a8c0e2d4f6a8b0c

Internally, HMAC does not simply concatenate the key and message. It uses a two-pass hashing process with inner and outer padding to prevent length-extension attacks that would be possible with naive concatenation. The formula is:

HMAC(K, m) = H((K' ⊕ opad) || H((K' ⊕ ipad) || m))

Where:
  K  = secret key
  m  = message
  H  = hash function (SHA-256, SHA-512, etc.)
  K' = key derived from K (padded or hashed to block size)
  ⊕  = XOR operation
  || = concatenation

Choosing an Algorithm

HMAC can be used with any cryptographic hash function. The most common choices are:

  • HMAC-SHA256 — The most widely used variant. Used by Stripe, GitHub, AWS Signature Version 4, and most modern APIs. Produces a 64-character hex digest. This is the safe default choice.
  • HMAC-SHA512 — Produces a 128-character hex digest. Offers a larger security margin and is actually faster than HMAC-SHA256 on 64-bit processors. Used by some financial APIs and high-security applications.
  • HMAC-SHA1 — Produces a 40-character hex digest. While SHA-1 alone is considered broken for collision resistance, HMAC-SHA1 is still considered secure due to the way HMAC constructs the hash. However, new implementations should prefer SHA-256 or SHA-512.
  • HMAC-MD5 — Deprecated and should not be used in new code. MD5 has known vulnerabilities, and while HMAC-MD5 is technically still resistant to known attacks, there is no reason to choose it over SHA-256.

API Authentication with HMAC

HMAC is commonly used to authenticate API requests. The typical flow works as follows:

  1. Shared secret. The API provider generates a secret key and shares it with the client during API key creation. Both parties store the key securely.
  2. Signing the request. Before sending a request, the client constructs a string to sign (usually containing the HTTP method, URL path, timestamp, and request body), then computes the HMAC using the shared secret.
  3. Sending the signature. The client includes the HMAC signature in the request, typically in an HTTP header like X-Signature or Authorization.
  4. Verification. The server receives the request, reconstructs the same string, computes its own HMAC using the stored secret, and compares the two signatures. If they match, the request is authenticated.
// Typical webhook verification flow
Client sends:
  POST /webhook
  X-Signature: sha256=a4b93f2b6c8e3d...
  X-Timestamp: 1707580800
  Body: {"event": "order.completed", "id": "ord_123"}

Server verifies:
  1. Extract timestamp from header
  2. Check timestamp is within 5 minutes (prevents replay attacks)
  3. Construct string: timestamp + "." + body
  4. Compute HMAC-SHA256(secret, string)
  5. Compare computed signature with X-Signature header
  6. If match → process webhook; if not → reject with 401

Node.js Example (crypto.createHmac)

Node.js includes HMAC support in the built-in crypto module. No external packages are needed:

import crypto from "node:crypto";

// Generate an HMAC-SHA256 signature
const secret = "your-webhook-secret";
const payload = '{"event":"order.completed","id":"ord_123"}';

const signature = crypto
  .createHmac("sha256", secret)
  .update(payload)
  .digest("hex");

console.log(signature);
// → "a4b93f2b6c8e3d1f5a9e7c2d8b4f6e0a..."

// Verify a webhook signature (timing-safe comparison)
function verifyWebhook(payload, receivedSig, secret) {
  const expectedSig = crypto
    .createHmac("sha256", secret)
    .update(payload)
    .digest("hex");

  const expected = Buffer.from(expectedSig, "hex");
  const received = Buffer.from(receivedSig, "hex");

  if (expected.length !== received.length) return false;
  return crypto.timingSafeEqual(expected, received);
}

// Express middleware example
app.post("/webhook", (req, res) => {
  const signature = req.headers["x-signature"];
  const isValid = verifyWebhook(
    JSON.stringify(req.body),
    signature,
    process.env.WEBHOOK_SECRET
  );

  if (!isValid) return res.status(401).send("Invalid signature");
  // Process webhook...
  res.status(200).send("OK");
});

Python Example (hmac module)

Python's standard library includes the hmac module:

import hmac
import hashlib
import json

# Generate an HMAC-SHA256 signature
secret = b"your-webhook-secret"
payload = b'{"event":"order.completed","id":"ord_123"}'

signature = hmac.new(secret, payload, hashlib.sha256).hexdigest()
print(signature)
# → "a4b93f2b6c8e3d1f5a9e7c2d8b4f6e0a..."

# Generate HMAC-SHA512
signature_512 = hmac.new(secret, payload, hashlib.sha512).hexdigest()

# Verify a webhook signature (timing-safe comparison)
def verify_webhook(payload: bytes, received_sig: str, secret: bytes) -> bool:
    expected_sig = hmac.new(secret, payload, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected_sig, received_sig)

# Flask example
from flask import Flask, request, abort

app = Flask(__name__)

@app.route("/webhook", methods=["POST"])
def webhook():
    signature = request.headers.get("X-Signature", "")
    is_valid = verify_webhook(
        request.data,
        signature,
        WEBHOOK_SECRET.encode()
    )

    if not is_valid:
        abort(401)

    data = request.get_json()
    # Process webhook...
    return "OK", 200

Security Best Practices

  • Use timing-safe comparison. Never compare signatures with === or ==. Standard string comparison leaks timing information that allows attackers to guess the signature one character at a time. Use crypto.timingSafeEqual() in Node.js or hmac.compare_digest() in Python.
  • Include a timestamp. Always include a timestamp in the signed payload and reject requests older than 5 minutes. This prevents replay attacks where an attacker captures a valid request and resends it later.
  • Keep secrets out of code. Store HMAC secrets in environment variables or a secrets manager (AWS Secrets Manager, HashiCorp Vault, etc.). Never commit secrets to version control.
  • Use sufficiently long keys. The key should be at least as long as the hash output (32 bytes for SHA-256, 64 bytes for SHA-512). Use a cryptographically secure random generator to create keys.
  • Rotate keys periodically. Implement key rotation so that a compromised key has a limited window of exposure. During rotation, accept signatures from both the old and new keys.
  • Sign the entire payload. Sign the complete request body, not just a subset of fields. If you only sign some fields, an attacker can modify the unsigned fields without invalidating the signature.
  • Log verification failures. Monitor failed signature verifications as potential attack indicators. Alert on spikes in failed verifications.

Using the PulpMiner HMAC Generator

When debugging webhook integrations or testing API authentication, you often need to quickly compute an HMAC signature for a given payload and secret. The PulpMiner HMAC Generator lets you enter a message and secret key, select your algorithm (SHA-1, SHA-256, SHA-384, or SHA-512), and instantly see the resulting HMAC in hex format. It runs entirely in your browser — your secret key never leaves your machine.

Need to extract data from websites?

PulpMiner turns any webpage into structured JSON data. No scraping code needed.

Try PulpMiner Free

No credit card required