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
Thesignature field is included in every webhook payload:
How Signatures Are Generated
StableStack generates signatures as follows:-
Build the payload object (without the
signaturefield): -
Serialize it to a JSON string (
payloadString = JSON.stringify(payload)) -
Build the signed message:
-
Compute
HMAC-SHA256(message, signingSecret)and hex-encode the result -
Attach to the payload as:
Verifying Signatures
Node.js
Python
Replay Attack Prevention
Thet timestamp in the signature allows you to reject events that are replayed after a delay. StableStack recommends rejecting any event older than 5 minutes.
Best Practices
Store your signing secret securely
Store your signing secret securely
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.Always use constant-time comparison
Always use constant-time comparison
Use
crypto.timingSafeEqual (Node.js) or hmac.compare_digest (Python) when comparing signatures. Standard string equality (===) is vulnerable to timing attacks.Respond before processing
Respond before processing
Return a
200 response as soon as signature verification passes. Process the event asynchronously to avoid timeouts that could trigger unnecessary retries.Log failed verifications
Log failed verifications
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:Rotating Your Signing Secret
If your signing secret is compromised:- Go to Dashboard → Settings → Webhook
- Click Signing Secret
- Update your environment variable with the new secret
- The old secret is invalidated immediately