QuickNode Webhook Guard Lacks Timestamp Validation
QuickNode webhook guard verified HMAC signature but did not validate the timestamp against a time window. Captured valid payloads could be replayed indefinitely. Fixed with a ±300s symmetric timestamp window.
Description
The guard verifies the HMAC signature but does not validate the timestamp header against a time window.
// quicknode-webhook-signature.guard.ts L29-L47const signature = request.headers[quicknodeSignatureHeader];const nonce = request.headers[quicknodeNonceHeader];const timestamp = request.headers[quicknodeTimestampHeader];// L31, timestamp extractedif (typeof signature !== "string") { throw new UnauthorizedException(...); }if (typeof nonce !== "string" || typeof timestamp !== "string") { throw new UnauthorizedException(...); }const expectedSignature = createHmac("sha256", this.quicknodeConfig.webhookSigningKey).update(nonce).update(timestamp) // L43, timestamp included in HMAC but never validated for recency.update(rawBody).digest("hex");assertWebhookSignaturesMatch(expectedSignature, signature); // L47, signature valid, but could be hours/days oldreturn true;
No check like if (Math.abs(Date.now() - parseInt(timestamp)) > maxAge) throw ... exists. A captured valid payload can be replayed indefinitely.
Impact
Captured webhook payloads can be replayed at any future point, potentially triggering duplicate processing of on-chain events.
Recommendation
Validate that the timestamp is within an acceptable window (e.g., ±300 seconds).
Resolution
Fixed in PR #3742, added a ±300s symmetric timestamp window check that runs before the HMAC computation.

