Verify webhook signatures
Validate product webhook HMAC headers before parsing or processing webhook events.
Every source-backed 0Gate webhook includes a Gate-Signature header in the form t=<unix timestamp>,v1=<hex hmac>. The HMAC is computed over <timestamp>.<raw body>. Other 0Bit product surfaces must use the signature header and secret documented in their API reference. Your handler must verify the product signature before trusting the payload.
Raw body is mandatory
Do not parse JSON before verification. Framework body parsers can change whitespace or key ordering, which makes the signature fail or encourages unsafe verification shortcuts.
Verification flow
Reference implementation
import crypto from 'node:crypto';
function verifyWebhookSignature(input: {
rawBody: string;
signature: string;
secret: string;
skewSeconds?: number;
}) {
const skewSeconds = input.skewSeconds ?? 300;
const parts = Object.fromEntries(
input.signature.split(',').map((part) => {
const [key, value] = part.split('=');
return [key, value];
}),
);
const timestamp = Number(parts.t);
if (!Number.isInteger(timestamp)) throw new Error('invalid_signature');
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - timestamp) > skewSeconds) {
throw new Error('stale_signature');
}
const expected = crypto
.createHmac('sha256', input.secret)
.update(`${timestamp}.${input.rawBody}`)
.digest('hex');
const received = parts.v1 ?? '';
const expectedBuffer = Buffer.from(expected, 'hex');
const receivedBuffer = Buffer.from(received, 'hex');
if (
expectedBuffer.length !== receivedBuffer.length ||
!crypto.timingSafeEqual(expectedBuffer, receivedBuffer)
) {
throw new Error('signature_mismatch');
}
return JSON.parse(input.rawBody);
}Failure handling
| Failure | Response | Log |
|---|---|---|
| Missing signature | 401 | Request id, timestamp, no body. |
| Stale timestamp | 401 | Timestamp drift, no body. |
| HMAC mismatch | 401 | Mismatch reason, no signature value. |
| Invalid JSON after valid signature | 400 | Event id if present, redacted body summary. |
Common mistakes
- Verifying the parsed JSON instead of the raw request body.
- Using the live webhook secret for sandbox deliveries, or the reverse.
- Comparing HMAC strings with
===instead of a constant-time comparator. - Logging unverified bodies, signature headers, or webhook secrets.
- Treating signature verification as optional during local testing.