Verify payment webhook signatures

Node · Node · advanced · greenfield

Adds signature verification for the payment webhook: computes the HMAC over the raw body and compares it to the header in constant time, then accepts or rejects. Small and direct.

Requirements

Files touched

--- src/lib/verifySignature.js
+const crypto = require("crypto");
+
+const HEX_SHA256 = /^[0-9a-f]{64}$/i;
+
+function verifySignature(rawBody, signatureHex, secret) {
+  if (!Buffer.isBuffer(rawBody) || !HEX_SHA256.test(signatureHex)) {
+    return false;
+  }
+  const expected = crypto.createHmac("sha256", secret).update(rawBody).digest();
+  return crypto.timingSafeEqual(Buffer.from(signatureHex, "hex"), expected);
+}
+
+module.exports = { verifySignature };
--- src/routes/webhooks.js
+const express = require("express");
+const { verifySignature } = require("../lib/verifySignature");
+const { WEBHOOK_SECRET } = require("../config/secrets");
+
+const router = express.Router();
+
+router.post("/webhooks/payments", express.raw({ type: "application/json" }), (req, res) => {
+  const signature = req.get("X-Signature") || "";
+  if (!verifySignature(req.body, signature, WEBHOOK_SECRET)) {
+    return res.status(401).json({ error: "invalid signature" });
+  }
+  return res.status(202).json({ received: true });
+});
+
+module.exports = router;

Review this PR

Node practice