HomeGuidesHow to Verify Webhook Signatures with HMAC-SHA256 Client-Side
Security & Privacy8 min readUpdated 2026-11-06

How to Verify Webhook Signatures with HMAC-SHA256 Client-Side

Learn how modern payment platforms like Stripe and GitHub sign webhook payloads, how to compute cryptographic HMAC digests, and how to prevent timing attacks.

Need to do this immediately?

Use our free, private client-side browser tool with zero installation.

Launch In-Browser HMAC Verifier
Webhooks are HTTP POST callbacks that notify your backend when events happen in external systems—such as a successful Stripe payment, a GitHub push, or a Shopify order. Without cryptographic signature verification, an attacker could forge fake webhook payloads and trigger unauthorized account upgrades or order shipments.

The Anatomy of HMAC-SHA256 Signature Headers

Platforms deliver signatures in HTTP headers (e.g., Stripe-Signature: t=1614552000,v1=5257a863e7... or X-Hub-Signature-256: sha256=...). The payload consists of a timestamp combined with the raw HTTP request body bytes.

The sending server uses a shared secret key and the HMAC (Hash-based Message Authentication Code) algorithm with SHA-256 to generate a unique hexadecimal hash.

Preventing Timing Attacks with Constant-Time Equality

A critical vulnerability in naive webhook verification is using standard JavaScript equality (`signature === computedSignature`). Standard string comparison exits on the first mismatched character, allowing attackers to measure microsecond timing discrepancies and guess the signature character-by-character.

Always use constant-time comparisons (like `crypto.timingSafeEqual` in Node.js or bitwise XOR loops in browser JavaScript) to guarantee identical execution time regardless of mismatch position.

Pro Tip:Always verify the webhook timestamp against the current server time to prevent replay attacks where an attacker re-sends an intercepted old payload.

Step-by-Step Tutorial

1

Capture Raw Payload Bytes

Read the raw body as an unparsed UTF-8 buffer before JSON.parse() strips whitespace or reorders keys.

2

Import Webhook Signing Secret

Use crypto.subtle.importKey() with algorithm HMAC and hash SHA-256 to import your secret key.

3

Compute and Verify Digest

Compute crypto.subtle.sign() and compare the resulting hex string using constant-time comparison.

Ready to Try It Yourself?

DocSwift processes everything directly on your device. Fast, safe, and completely free forever.

How helpful was this guide?

Rated 4.9 / 5 by 3,420+ users worldwide

Share with Colleagues & Friends

Help others protect their document privacy

Frequently Asked Questions

Why must I verify the raw body instead of parsed JSON?

Cryptographic hashes are bit-exact. Calling JSON.parse() and then JSON.stringify() changes key ordering and whitespace, yielding a completely different hash that fails verification.

Can I verify webhook payloads safely in the browser?

Yes! DocSwift Webhook Verifier uses the native Web Crypto API in local browser memory. Your private endpoint secrets never leave your device.