Signature Validation
Validate Blazelock webhook requests with the timestamp and signature headers.
Before your application trusts a webhook request, it should verify that the request really came from Blazelock and that the payload was not modified on the way to your endpoint. This helps confirm authenticity, protect against tampered payloads, and reduce the risk of replay attacks.
Headers
Each webhook request includes these headers:
| Header | Description |
|---|---|
X-Blazelock-Webhook-Timestamp | Unix timestamp in seconds indicating when the webhook request was sent. This value changes on each delivery attempt, including retries. |
X-Blazelock-Webhook-Signature | Hex-encoded HMAC-SHA256 signature calculated from {timestamp}.{raw_body} using your webhook secret. |
Your webhook secret is available in the Blazelock dashboard in the API integration settings.
Validation Steps
- Check the Timestamp: Verify that the timestamp from the
X-Blazelock-Webhook-Timestampheader is within an acceptable time window, such as 5 minutes, to help prevent replay attacks. - Build the Signed Payload: The signed payload is the exact concatenation of three parts: the
X-Blazelock-Webhook-Timestampheader value, a period (.), and the raw request body exactly as it was received.
Example:
1737830031.{"event":"file_scan.completed","integration_id":"...","occurred_at":"...","data":{...}}- Compute the Expected Signature: Generate an HMAC-SHA256 signature for the payload from step 2 using your webhook secret from the Blazelock dashboard.
- Compare the Signatures: Compare the signature you computed with the value from the
X-Blazelock-Webhook-Signatureheader using a constant-time comparison. - Process the Event: Only process the webhook event after the signature check succeeds.
Code Examples
The following examples focus only on signature verification. They assume you already have the raw request body, the two headers, and your webhook secret.
import crypto from 'node:crypto'
export function validateBlazelockWebhook(rawBody, timestampHeader, signatureHeader, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(`${timestampHeader}.${rawBody}`)
.digest('hex')
const providedSignature = Buffer.from(signatureHeader)
const expectedSignatureBuffer = Buffer.from(expectedSignature)
if (providedSignature.length !== expectedSignatureBuffer.length) {
return false
}
return crypto.timingSafeEqual(providedSignature, expectedSignatureBuffer)
}