diff --git a/NodeSrv/apps/healthcheck-survey-bot/src/lib/signatureVerify.js b/NodeSrv/apps/healthcheck-survey-bot/src/lib/signatureVerify.js new file mode 100644 index 00000000..5fc47d3b --- /dev/null +++ b/NodeSrv/apps/healthcheck-survey-bot/src/lib/signatureVerify.js @@ -0,0 +1,24 @@ +const crypto = require("node:crypto"); + +function normalizeSignature(value) { + return String(value || "").trim().replace(/^sha256=/i, ""); +} + +function safeEqual(a, b) { + const ab = Buffer.from(String(a), "utf8"); + const bb = Buffer.from(String(b), "utf8"); + if (ab.length !== bb.length) return false; + return crypto.timingSafeEqual(ab, bb); +} + +function verifySignature(rawBody, headerSignature, botSecret) { + const headerSig = normalizeSignature(headerSignature); + if (!headerSig || !botSecret) return false; + + const payload = Buffer.isBuffer(rawBody) ? rawBody : Buffer.from(String(rawBody), "utf8"); + const expected = crypto.createHmac("sha256", botSecret).update(payload).digest("base64"); + + return safeEqual(headerSig, expected); +} + +module.exports = { verifySignature }; diff --git a/NodeSrv/apps/healthcheck-survey-bot/test/signatureVerify.test.js b/NodeSrv/apps/healthcheck-survey-bot/test/signatureVerify.test.js new file mode 100644 index 00000000..bc8776ab --- /dev/null +++ b/NodeSrv/apps/healthcheck-survey-bot/test/signatureVerify.test.js @@ -0,0 +1,27 @@ +// test/signatureVerify.test.js +const { test } = require("node:test"); +const assert = require("node:assert"); +const crypto = require("node:crypto"); +const { verifySignature } = require("../src/lib/signatureVerify"); + +test("正しい署名はtrueを返す", () => { + const secret = "test-secret"; + const body = JSON.stringify({ hello: "world" }); + const signature = crypto.createHmac("sha256", secret).update(body).digest("base64"); + assert.strictEqual(verifySignature(body, signature, secret), true); +}); + +test("sha256=プレフィックス付き署名も検証できる", () => { + const secret = "test-secret"; + const body = JSON.stringify({ hello: "world" }); + const signature = crypto.createHmac("sha256", secret).update(body).digest("base64"); + assert.strictEqual(verifySignature(body, `sha256=${signature}`, secret), true); +}); + +test("不正な署名はfalseを返す", () => { + assert.strictEqual(verifySignature("body", "invalid-signature", "secret"), false); +}); + +test("署名ヘッダーが空ならfalseを返す", () => { + assert.strictEqual(verifySignature("body", "", "secret"), false); +});