Webhook签名验证必须在服务端完成,因前端无法安全保管密钥;浏览器中即使可用Crypto.subtle,密钥暴露风险仍致验证不可行;Node.js需用express.raw获取原始body,crypto.createHmac计算并timingSafeEqual比对。

HTML里没法直接做Webhook签名验证
浏览器环境无法安全完成Webhook签名验证——HMAC计算本身虽可做,但密钥一旦暴露在前端(哪怕藏在JS里),就等于公开。所有主流平台(GitHub、Stripe、Slack)都明确要求签名验证必须在服务端完成。
如果你看到“HTML实现HMAC”示例,基本是教学演示或本地调试用,上线即失效。真要验证,得把校验逻辑放到Node.js、Python、PHP等后端环境里。
为什么不能用crypto.subtle在前端验签名
虽然现代浏览器支持crypto.subtle.importKey和crypto.subtle.sign,但问题不在能力,而在密钥生命周期:
- 密钥必须以
ArrayBuffer形式传入,意味着你要在JS里硬编码或从API拉取——前者明文泄露,后者等于把密钥当公开参数发出去 -
SubtleCrypto.sign返回的是二进制签名,而Webhook通常用hex或base64格式比对,容易因编码不一致导致invalid signature - GitHub发送的
X-Hub-Signature-256头值是sha256=xxx格式,你得手动截掉前缀再比对,少一步就401
Node.js里用crypto.createHmac验GitHub Webhook最简路径
这是生产环境最常用、最不容易翻车的做法。核心就三步:取原始body、拼签名头、用密钥算HMAC。
生成可在 deepbi 沙箱 iframe 内运行或作为独立文件运行的交互式 HTML 页面,使用 `window.deepbi` JS SDK 实现数据查询、空间管理、文件浏览及用户操作。当用户提出以下需求时启用该能力: - 创建 KPI 仪表盘、数据表格、文件浏览器、成员列表、管理页面; - 明确要求“转成 dhtml”、“转译成 HTML”、“把 XX 页面转成……”、“控制台页面”、“管理后台”、“DeepBI 页面迁移”; - 提及位于 `apps/console/src/featur
- 务必用
express.raw({ type: 'application/json' })中间件接收原始字节流——如果用express.json(),body会被JSON解析再字符串化,破坏原始换行/空格,签名必然失败 - GitHub密钥是字符串,直接传给
crypto.createHmac('sha256', 'your-webhook-secret'),别转Buffer - 签名比对用
crypto.timingSafeEqual(),不用===——防时序攻击,否则可能被绕过
const hmac = crypto.createHmac('sha256', secret);
hmac.update(rawBody); // rawBody是Buffer
const digest = 'sha256=' + hmac.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signatureHeader))) {
return res.status(401).end();
}
常见错误现象和对应检查点
签名总不通过?先盯这几个地方:
-
rawBody为空或长度为0 → 检查是否漏了express.raw(),或前端发错了Content-Type(必须是application/json,不是text/plain) - 报错
Error: Invalid key length→ 密钥变量是null或undefined,打印出来确认 - 本地测试OK但线上失败 → 云函数(如Vercel/Cloudflare Workers)默认body是字符串,需显式转
Uint8Array再喂给createHmac - 用Postman模拟时签名错 → Postman的
raw → JSON模式会自动加换行和空格,改用raw → Text并手动输入无格式JSON
真正卡住的,往往不是算法,而是body到底有没有被谁悄悄动过——多打一行console.log(rawBody.length),比查文档快。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










