Skip to main content

Overview

Every webhook sent by StableStack is signed with HMAC-SHA256 using your endpoint’s unique signing secret. Verifying signatures ensures that:
  • The request originated from StableStack
  • The payload has not been tampered with in transit
  • The request is not a replay of an older event

The Signature Header

The signature field is included in every webhook payload:
It contains two comma-separated components:

How Signatures Are Generated

StableStack generates signatures as follows:
  1. Build the payload object (without the signature field):
  2. Serialize it to a JSON string (payloadString = JSON.stringify(payload))
  3. Build the signed message:
  4. Compute HMAC-SHA256(message, signingSecret) and hex-encode the result
  5. Attach to the payload as:

Verifying Signatures

Node.js

Express.js example:

Python


Replay Attack Prevention

The t timestamp in the signature allows you to reject events that are replayed after a delay. StableStack recommends rejecting any event older than 5 minutes.
If your server’s clock is significantly out of sync, legitimate events may be rejected. Ensure your server uses NTP time synchronisation.

Best Practices

Never hardcode your signing_secret in source code or expose it client-side. Store it as an environment variable and rotate it immediately if compromised.
Use crypto.timingSafeEqual (Node.js) or hmac.compare_digest (Python) when comparing signatures. Standard string equality (===) is vulnerable to timing attacks.
Return a 200 response as soon as signature verification passes. Process the event asynchronously to avoid timeouts that could trigger unnecessary retries.
Track signature failures to detect misconfiguration or attack attempts:

Testing Signature Verification

You can test your verification logic using the example payload and the signing secret from your dashboard:
Use the Dashboard to send test events to your endpoint and inspect delivery logs.

Rotating Your Signing Secret

If your signing secret is compromised:
  1. Go to Dashboard → Settings → Webhook
  2. Click Signing Secret
  3. Update your environment variable with the new secret
  4. The old secret is invalidated immediately
Rotating your secret will cause all in-flight webhooks signed with the old secret to fail verification. Update your application before rotating in production.