RunOfflineTools Logo RunOfflineTools
πŸ” Webhook Security & HMAC Signatures

Webhook HMAC Signer & Signature Verifier

Test, sign, and troubleshoot webhook authentication headers for Stripe, GitHub, Shopify, Slack, and custom HMAC-SHA256 endpoints.

πŸ” Payload & Secret Parameters

100% In-Browser Crypto

🎯 Generated Signature Header

This is the exact signature string expected in HTTP headers:

Enter payload and secret to generate...

πŸ§ͺ Test & Verify Incoming Header

Paste the signature from your webhook HTTP header to check validity:

πŸ“¦ Production Verification Code

Timing-safe comparison snippets to securely verify signatures on your backend.

// Node.js Stripe Webhook Signature Verification
const crypto = require('crypto');

function verifyStripeWebhook(payload, signatureHeader, secret, tolerance = 300) {
  const parts = signatureHeader.split(',');
  const timestamp = parts.find(p => p.startsWith('t='))?.split('=')[1];
  const signature = parts.find(p => p.startsWith('v1='))?.split('=')[1];

  if (!timestamp || !signature) return false;

  // Check timestamp tolerance to prevent replay attacks
  const now = Math.floor(Date.now() / 1000);
  if (Math.abs(now - parseInt(timestamp, 10)) > tolerance) return false;

  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${payload}`)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expectedSignature, 'hex')
  );
}

The Critical Role of Webhook Verification

Webhooks power real-time asynchronous architectures: notifying your backend when a payment succeeds on Stripe, an order is placed on Shopify, or code is pushed to GitHub. However, because webhook listener endpoints are publicly accessible URLs, any bad actor could send fraudulent HTTP POST requests to trigger orders, credit accounts, or execute pipelines unless verified.

How Webhook HMAC Signatures Work

When a provider sends a webhook, it takes your private shared webhook secret and computes a cryptographic hash over the request body using HMAC-SHA256:

Best Practices for Implementing Webhook Handlers

  1. Always Preserve the Raw Body: Never parse JSON before calculating the signature. Even a single added space or newline will invalidate the cryptographic hash.
  2. Enforce Replay Tolerance: Reject any webhook where the embedded timestamp deviates by more than 300 seconds (5 minutes) from your server's current clock.
  3. Use Timing-Safe Comparison: Never use === or == when comparing signatures. Always use constant-time functions like crypto.timingSafeEqual().

Frequently Asked Questions

Why do webhooks use HMAC cryptographic signatures?

HMAC (Hash-based Message Authentication Code) ensures that an incoming webhook request genuinely originated from the service provider (e.g. Stripe, GitHub, Shopify) and that the payload has not been intercepted, forged, or altered in transit by an attacker.

Why is a timestamp included in Stripe and Slack webhook signatures?

Timestamps protect your application against replay attacks. Even if an attacker intercepts a valid webhook payload and signature, your server will reject the request if the timestamp is older than your configured tolerance threshold (typically 5 minutes / 300 seconds).

Why must I verify webhook signatures using timing-safe comparisons?

Standard string equality operators ('===') exit immediately upon encountering the first mismatched character, creating subtle microsecond execution time differences (timing attacks) that allow sophisticated attackers to guess secret signatures byte-by-byte. Timing-safe algorithms (like crypto.timingSafeEqual in Node.js or hmac.compare_digest in Python) compare the entire buffer in constant time.

Why does my webhook signature fail when testing locally with express.json()?

Common culprit! Webhook signatures are computed using the exact raw byte stream of the request body. If body-parser or express.json() parses and reformats the JSON payload (reordering keys or changing whitespace), the resulting HMAC hash will completely change. Always capture req.rawBody or use express.raw({ type: 'application/json' }) before JSON parsing.

Are my webhook secrets or payloads uploaded to your server?

No. 100% of cryptographic calculations are executed locally inside your browser's Web Cryptography API (window.crypto.subtle). Zero data is transmitted across the network.

Which hash algorithms are supported by this tool?

The tool natively supports HMAC-SHA256 (the industry standard used by Stripe, GitHub, Shopify, and Slack), as well as HMAC-SHA512 and HMAC-SHA1 with Hex or Base64 output encoding.