HMAC Webhook Signature Generator & Verifier
RFC 2104Compute HMAC-SHA256, SHA-384, and SHA-512 message authentication codes in Hex and Base64 locally in your browser.
1. Secret Key, Hash Algorithm & Raw Payload
2. Constant-Time Signature Verifier
import crypto from "node:crypto";
const SECRET_KEY = "whsec_8f92a7d1e4b6c30918273645fedcba98";
const RAW_BODY = "{\"id\":\"evt_3Qa8K2LkdIwHu7ix\",\"object\":\"event\",\"type\":\"payment_intent.succeeded\",\"created\":1730900400}";
// 1. Compute RFC 2104 HMAC-SHA-256
const computedHex = crypto
.createHmac("sha256", SECRET_KEY)
.update(RAW_BODY, "utf8")
.digest("hex");
// 2. Constant-Time Webhook Signature Verification
export function verifyWebhookSignature(receivedHeader) {
const cleanHeader = receivedHeader.replace(/^sha256=/, "");
const a = Buffer.from(computedHex, "hex");
const b = Buffer.from(cleanHeader, "hex");
if (a.length !== b.length) return false;
return crypto.timingSafeEqual(a, b);
}
// Expected Hex:
// Expected Base64: Keyed-Hashing for Message Authentication (RFC 2104) & Webhook Security
Hash-based Message Authentication Code (HMAC), standardized in IETF RFC 2104 and FIPS PUB 198-1, combines a cryptographic hash function (SHA-256 or SHA-512) with a secret symmetric key using two nested passes XORed with inner (0x36) and outer (0x5c) padding constants. Unlike a naive SHA256(secret + message) construction, HMAC is immune to Merkle-Damgård length-extension attacks.
Defending Against Timing Side-Channel Attacks
Never compare an incoming webhook signature header against your computed HMAC using standard string equality (=== or ==). Standard equality operators short-circuit on the first mismatched byte, leaking nanosecond timing variations that allow attackers to forge valid signatures byte-by-byte. Always use constant-time comparison functions such as crypto.timingSafeEqual() in Node.js or hmac.compare_digest() in Python.
Preventing Replay Attacks with Timestamp Binding
Providers like Stripe and Slack prepend the Unix epoch timestamp to the raw HTTP request body before signing ("{timestamp}.{raw_body}"). Your webhook consumer must verify both the cryptographic MAC and assert that the timestamp falls within a strict tolerance window (typically 300 seconds) to prevent replay of captured requests.