Signature Verification
Most webhook providers sign their payloads so you can verify authenticity. HookNexus includes a built-in Signature Verification tool and a public API endpoint for this purpose.
Why Verify Signatures?
Without verification, anyone who knows your webhook URL can send fake events. Signature verification ensures:
- The request genuinely came from the provider
- The payload hasn’t been tampered with in transit
Using the Dashboard Tool
- Select a request in the Dashboard.
- Click the Verify Signature tab in the detail panel.
- Choose the provider (Stripe, GitHub, Shopify, or Slack).
- Enter your webhook secret.
- The tool shows whether the signature is valid or invalid, along with the computed hash.
Using the API
Send a POST request to the public verification endpoint:
curl -X POST https://api.hooknexus.com/api/verify-signature \ -H "Content-Type: application/json" \ -d '{ "provider": "stripe", "payload": "raw request body here", "signature": "t=1704278400,v1=abc123...", "secret": "whsec_your_stripe_secret" }'Response:
{ "valid": true, "provider": "stripe", "algorithm": "sha256"}Provider Details
Header: Stripe-Signature
Format: t=timestamp,v1=signature
Algorithm: HMAC-SHA256
Stripe includes a timestamp to prevent replay attacks. The signed payload is {timestamp}.{body}.
const signedPayload = `${timestamp}.${rawBody}`;const expectedSig = crypto .createHmac('sha256', secret) .update(signedPayload) .digest('hex');Header: X-Hub-Signature-256
Format: sha256=hexdigest
Algorithm: HMAC-SHA256
const expectedSig = 'sha256=' + crypto .createHmac('sha256', secret) .update(rawBody) .digest('hex');Header: X-Shopify-Hmac-Sha256
Format: Base64-encoded digest
Algorithm: HMAC-SHA256
const expectedSig = crypto .createHmac('sha256', secret) .update(rawBody) .digest('base64');Header: X-Slack-Signature
Format: v0=hexdigest
Algorithm: HMAC-SHA256
Slack includes a timestamp in X-Slack-Request-Timestamp. The signed payload is v0:{timestamp}:{body}.
const signedPayload = `v0:${timestamp}:${rawBody}`;const expectedSig = 'v0=' + crypto .createHmac('sha256', secret) .update(signedPayload) .digest('hex');Troubleshooting
| Issue | Cause | Fix |
|---|---|---|
| Signature always invalid | Wrong secret key | Double-check your webhook secret from the provider’s dashboard |
| Stripe signature fails | Using parsed body instead of raw | Always use the raw request body string, not parsed JSON |
| Timestamp mismatch | Clock skew | Stripe allows 5-minute tolerance by default |
| Base64 vs hex confusion | Shopify uses Base64, others use hex | The HookNexus tool handles this automatically |