Recuro.

Webhook Signature

Quick Summary — TL;DR

  • A webhook signature is a cryptographic hash (usually HMAC-SHA256) that proves a webhook request is authentic and untampered.
  • The sender signs the request body with a shared secret; your server recomputes the hash and compares.
  • Always use constant-time comparison to prevent timing attacks, and verify against the raw request body before parsing.

A webhook signature is a cryptographic hash sent alongside a webhook request that proves the request came from the expected sender and hasn't been tampered with in transit. Without signature verification, anyone who discovers your webhook URL can send fake events to your application.

How webhook signatures work

The most common approach uses HMAC-SHA256:

  1. Shared secret — when you register your webhook URL, the provider gives you a secret key. Both sides know this key, but it's never sent in the request itself.
  2. Signing — before sending the webhook, the provider computes an HMAC-SHA256 hash of the raw request body using the shared secret. The resulting signature is included in a header (e.g., X-Signature-256, Stripe-Signature).
  3. Verification — your server receives the request, computes the same HMAC-SHA256 hash using your copy of the secret and the raw request body, and compares it to the signature in the header. If they match, the request is authentic.

Why raw body matters

The signature is computed over the exact bytes of the request body. If your web framework parses the JSON body before you access it, and you re-serialize it for verification, the bytes may differ (key ordering, whitespace). Always capture the raw body before parsing.

Implementation example

The pattern is the same across languages:

  1. Read the raw request body as a string or byte array
  2. Compute HMAC-SHA256(secret, raw_body)
  3. Compare the computed hash to the signature header using a constant-time comparison function
  4. Reject the request with 401 if the signatures don't match

Constant-time comparison

Never compare signatures with == or ===. Standard string comparison returns early on the first mismatched character, which leaks timing information an attacker can exploit. Use a constant-time comparison function like crypto.timingSafeEqual() (Node.js), hmac.compare_digest() (Python), or hash_equals() (PHP).

Common signature header names

Provider Header Algorithm
StripeStripe-SignatureHMAC-SHA256 with timestamp
GitHubX-Hub-Signature-256HMAC-SHA256
ShopifyX-Shopify-Hmac-SHA256HMAC-SHA256 (Base64)
TwilioX-Twilio-SignatureHMAC-SHA1
SlackX-Slack-SignatureHMAC-SHA256 with timestamp

Replay attack protection

A signature proves authenticity, but an attacker could capture a legitimate signed request and re-send it later. To prevent this, some providers include a timestamp in the signature payload (e.g., Stripe and Slack). Your server should reject requests where the timestamp is more than a few minutes old.

FAQ

What is a webhook signature?

A webhook signature is a cryptographic hash (usually HMAC-SHA256) attached to a webhook request that lets you verify the request came from the expected sender and wasn't modified in transit.

What happens if I don't verify webhook signatures?

Anyone who knows your webhook URL can send fake events. This can lead to unauthorized actions — fake payment confirmations, spoofed user events, or data corruption.

Do I need to verify signatures in development?

You can skip verification for local testing, but always enforce it in production. Most webhook libraries have a "skip verification" flag for development — just make sure it's never enabled in production.

Webhook signatures protect your webhook endpoints from spoofed requests. They're especially critical for inbound webhooks where the webhook event originates from an external service. Verification must happen before processing the payload, and handlers should remain idempotent in case duplicate signed requests arrive during retries.

Stop managing infrastructure. Start scheduling jobs.

Recuro handles cron scheduling, retries, alerts, and execution logs -- so you can focus on building your product.

No credit card required