Signature Verification
Upon receiving the request at your endpoint, it is recommended to verify the signature to ensure the request's authenticity and integrity. This can be done by comparing the x-subrite-webhook-signature
header with a computed signature based on your webhook secret and the request payload. The x-subrite-webhook-signature-timestamp
header can be used to prevent replay attacks by ensuring the request is within an acceptable time frame.
Verification Steps
Here’s a basic outline of steps for signature verification:
-
Retrieve the Signature and Timestamp from Headers:
x-subrite-webhook-signature
x-subrite-webhook-signature-timestamp
-
Create a Signature String:
- Combine the timestamp and the request payload.
-
Compute the HMAC:
- Use your webhook secret and the combined string to compute an HMAC SHA256 hash.
-
Compare Signatures:
- Compare the computed hash with the received signature.
By implementing these steps, you can ensure that the request is genuinely from Subrite and has not been tampered with during transmission.
NestJS Example
Below is a sample implementation in TypeScript for verifying the signature of incoming webhook requests in a NestJS application. This example demonstrates how to extract the signature and timestamp from the request headers, compute the HMAC signature, and compare it to the received signature.
import { Controller, Post, Body, Headers, HttpException, HttpStatus } from '@nestjs/common';
import * as crypto from 'crypto';
@Controller('webhook')
export class WebhookController {
@Post('test')
testWebhook(@Body() body: unknown, @Headers() headers: Record<string, string>) {
const signature = headers['x-subrite-webhook-signature'] as string;
const timestamp = headers['x-subrite-webhook-signature-timestamp'] as string;
if (!this.verifySignature(signature, timestamp, JSON.stringify(body))) {
throw new HttpException('Invalid signature', HttpStatus.UNAUTHORIZED);
}
// Implement your logic
}
verifySignature(signature: string, timestamp: string, body: string = '') {
const secret = 'your-webhook-secret';
const algorithm = 'sha256';
const computedSignature = this.computeSignature(secret, algorithm, timestamp, body);
return computedSignature === signature;
}
computeSignature(
secret: string,
algorithm: string,
timestamp: string,
body: string = '',
): string {
const data = body || '';
const hmac = crypto.createHmac(algorithm, secret);
const signature = hmac.update(timestamp + data).digest('base64');
return signature;
}
}