From 48169bd3132b15236dac6f6b0906981b7af09952 Mon Sep 17 00:00:00 2001 From: wydady Date: Fri, 12 Jun 2026 23:34:49 +0100 Subject: [PATCH 1/2] fix(webhooks): harden signature verification and fix docs Fixes several issues in the webhooks overview doc: - Fixed broken URL syntax in the setup section - Added replay attack protection (reject requests older than 5 minutes) - Fixed a crash when signature header is malformed - Fixed a crash in timingSafeEqual when buffer lengths don't match - Reformatted setup steps using the Steps component --- webhooks/overview.mdx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/webhooks/overview.mdx b/webhooks/overview.mdx index 84cfb3a..14233ff 100644 --- a/webhooks/overview.mdx +++ b/webhooks/overview.mdx @@ -45,14 +45,23 @@ Every v1 webhook request includes an `x-topgg-signature` header in the format `t import crypto from 'crypto'; function verifyWebhook(rawBody: string, signature: string, secret: string): boolean { - const [tPart, v1Part] = signature.split(','); + const parts = signature.split(','); + if (parts.length !== 2) return false; + const [tPart, v1Part] = parts; const timestamp = tPart.split('=')[1]; const receivedSig = v1Part.split('=')[1]; + if (!timestamp || !receivedSig) return false; + const ts = parseInt(timestamp, 10); + const now = Math.floor(Date.now() / 1000); + if (isNaN(ts) || Math.abs(now - ts) > 300) return false; const expected = crypto .createHmac('sha256', secret) .update(`${timestamp}.${rawBody}`) .digest('hex'); - return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(receivedSig)); + const expectedBuf = Buffer.from(expected); + const receivedBuf = Buffer.from(receivedSig); + if (expectedBuf.length !== receivedBuf.length) return false; + return crypto.timingSafeEqual(expectedBuf, receivedBuf); } ``` From 1cbfc19db893d71497986fdac37a7c03f75a61f5 Mon Sep 17 00:00:00 2001 From: wydady Date: Fri, 12 Jun 2026 23:41:39 +0100 Subject: [PATCH 2/2] fix(webhooks): use order-independent signature header parsing --- webhooks/overview.mdx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/webhooks/overview.mdx b/webhooks/overview.mdx index 14233ff..dc51982 100644 --- a/webhooks/overview.mdx +++ b/webhooks/overview.mdx @@ -46,10 +46,16 @@ import crypto from 'crypto'; function verifyWebhook(rawBody: string, signature: string, secret: string): boolean { const parts = signature.split(','); - if (parts.length !== 2) return false; - const [tPart, v1Part] = parts; - const timestamp = tPart.split('=')[1]; - const receivedSig = v1Part.split('=')[1]; + let timestamp: string | undefined; + let receivedSig: string | undefined; + + for (const part of parts) { + const [key, ...valueParts] = part.split('='); + const value = valueParts.join('='); + if (key === 't') timestamp = value; + if (key === 'v1') receivedSig = value; + } + if (!timestamp || !receivedSig) return false; const ts = parseInt(timestamp, 10); const now = Math.floor(Date.now() / 1000);