SyncNexaSyncNexa
Docs
v1.0 (Latest)

Verifying Webhook Signatures

Security

Detailed security guide on validating HMAC-SHA256 signatures, preventing replay attacks, and debugging webhook payloads.

Last updated: August 2026

Verifying webhook signatures protects your backend against malicious actors spoofing verification events to illegitimately obtain student discounts or access privileges.

Why Signature Verification is Critical

Without signature validation, any bad actor who discovers your webhook URL could forge an HTTP POST payload claiming that a student is verified. SyncNexa signs every payload with HMAC-SHA256 using your unique webhook signing secret.

Anatomy of the Signature Header

The X-SyncNexa-Signature header contains two comma-separated key-value pairs:

sample-header.txtbash
1X-SyncNexa-Signature: t=1724155200,v1=5d41402abc4b2a76b9719d911017c5922e92c68e3768f5c9ef4780775d7b5394
  • t: Unix timestamp in seconds when the webhook was generated.
  • v1: Hexadecimal representation of the HMAC-SHA256 signature calculated over ${t}.${rawBody} using your endpoint secret.

Implementation Examples

typescript
1import crypto from 'crypto';
2 
3export function isSignatureValid(
4 rawBody: Buffer | string,
5 sigHeader: string,
6 secret: string
7): boolean {
8 const [tPart, v1Part] = sigHeader.split(',');
9 const timestamp = tPart?.split('=')[1];
10 const signature = v1Part?.split('=')[1];
11 
12 if (!timestamp || !signature) return false;
13 
14 const payload = `${timestamp}.${rawBody.toString('utf8')}`;
15 const expected = crypto
16 .createHmac('sha256', secret)
17 .update(payload)
18 .digest('hex');
19 
20 return crypto.timingSafeEqual(
21 Buffer.from(expected, 'utf8'),
22 Buffer.from(signature, 'utf8')
23 );
24}

Replay Attack Mitigation

Always check that the timestamp t is within a reasonable tolerance (e.g. 5 minutes or 300 seconds) of the current server time:

timestamp-guard.tstypescript
1const currentTimestamp = Math.floor(Date.now() / 1000);
2if (Math.abs(currentTimestamp - Number(timestamp)) > 300) {
3 throw new Error('Webhook timestamp outside allowed tolerance window.');
4}

Debugging Signature Failures

Raw Body vs Parsed JSON
Ensure you calculate the HMAC over the exact RAW unparsed HTTP body string or buffer. If your web framework parses JSON before computing the HMAC (e.g. `body-parser` formatting whitespace or key order), the calculated signature will not match.
Was this page helpful?