Reject malformed CoinPay webhook timestamps

This commit is contained in:
Codex Microtask Operator 2026-06-13 01:36:03 +02:00
parent 8f4691584c
commit f2be6b850d
2 changed files with 5 additions and 1 deletions

View file

@ -411,6 +411,10 @@ describe("POST /api/webhooks/coinpay", () => {
expect(verifyCoinPayWebhook(payload, `t=${timestamp},v1=${signature}`, secret)).toBe(true); expect(verifyCoinPayWebhook(payload, `t=${timestamp},v1=${signature}`, secret)).toBe(true);
expect(verifyCoinPayWebhook(payload, `t=${timestamp}, v1=${signature}`, secret)).toBe(true); expect(verifyCoinPayWebhook(payload, `t=${timestamp}, v1=${signature}`, secret)).toBe(true);
const malformedTimestamp = `${timestamp}abc`;
const malformedSignature = createHmac("sha256", secret).update(`${malformedTimestamp}.${payload}`).digest("hex");
expect(verifyCoinPayWebhook(payload, `t=${malformedTimestamp},v1=${malformedSignature}`, secret)).toBe(false);
const response = await coinpayWebhook( const response = await coinpayWebhook(
new NextRequest("http://localhost/api/webhooks/coinpay", { new NextRequest("http://localhost/api/webhooks/coinpay", {
method: "POST", method: "POST",

View file

@ -117,7 +117,7 @@ export function verifyCoinPayWebhook(
const parts = signatureHeader.split(",").map((part) => part.trim()); const parts = signatureHeader.split(",").map((part) => part.trim());
const timestamp = parts.find((part) => part.startsWith("t="))?.slice(2); const timestamp = parts.find((part) => part.startsWith("t="))?.slice(2);
const signature = parts.find((part) => part.startsWith("v1="))?.slice(3); const signature = parts.find((part) => part.startsWith("v1="))?.slice(3);
if (!timestamp || !signature) { if (!timestamp || !signature || !/^\d+$/.test(timestamp)) {
return false; return false;
} }