Skip to content

Commit be753cf

Browse files
authored
fix(pwa): require decimal signature timestamps (#66)
1 parent 1416c44 commit be753cf

2 files changed

Lines changed: 33 additions & 3 deletions

File tree

apps/pwa/src/lib/signature.mjs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import crypto from "node:crypto";
44
export function verifySignature(header, rawBody, secret, toleranceSec = 300) {
55
if (!secret || !header) return false;
66
const parts = Object.fromEntries(header.split(",").map((kv) => kv.split("=").map((s) => s.trim())));
7-
const ts = Number(parts.t);
7+
const rawTs = parts.t;
8+
if (!/^\d+$/.test(rawTs || "")) return false;
9+
const ts = Number(rawTs);
810
const v1 = parts.v1;
9-
if (!ts || !v1) return false;
11+
if (!Number.isSafeInteger(ts) || !ts || !v1) return false;
1012
if (Math.abs(Math.floor(Date.now() / 1000) - ts) > toleranceSec) return false;
11-
const expected = crypto.createHmac("sha256", secret).update(`${ts}.${rawBody}`).digest("hex");
13+
const expected = crypto.createHmac("sha256", secret).update(`${rawTs}.${rawBody}`).digest("hex");
1214
const a = Buffer.from(v1);
1315
const b = Buffer.from(expected);
1416
return a.length === b.length && crypto.timingSafeEqual(a, b);

apps/pwa/test/signature.test.mjs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import assert from "node:assert/strict";
2+
import crypto from "node:crypto";
3+
import test from "node:test";
4+
import { verifySignature } from "../src/lib/signature.mjs";
5+
6+
const secret = "test-secret";
7+
const rawBody = JSON.stringify({ ok: true });
8+
9+
function sign(ts) {
10+
return crypto.createHmac("sha256", secret).update(`${ts}.${rawBody}`).digest("hex");
11+
}
12+
13+
test("verifySignature accepts a valid decimal timestamp signature", () => {
14+
const ts = String(Math.floor(Date.now() / 1000));
15+
assert.equal(verifySignature(`t=${ts},v1=${sign(ts)}`, rawBody, secret), true);
16+
});
17+
18+
test("verifySignature rejects scientific notation timestamps", () => {
19+
const ts = "1e9";
20+
const normalized = String(Number(ts));
21+
const sig = crypto.createHmac("sha256", secret).update(`${normalized}.${rawBody}`).digest("hex");
22+
assert.equal(verifySignature(`t=${ts},v1=${sig}`, rawBody, secret, 9_999_999_999), false);
23+
});
24+
25+
test("verifySignature rejects fractional timestamps", () => {
26+
const ts = `${Math.floor(Date.now() / 1000)}.5`;
27+
assert.equal(verifySignature(`t=${ts},v1=${sign(ts)}`, rawBody, secret), false);
28+
});

0 commit comments

Comments
 (0)