You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Node.js crypto模块实现PHP hash_hmac验证Webhook签名

Webhook签名验证:JS与PHP不匹配的解决方案

问题根源

PHP代码直接读取原始请求体(php://input)计算HMAC,而你的JavaScript代码用JSON.stringify(req.body)生成签名内容——这会导致两个核心差异:

  • 框架(如Express)解析req.body时,可能自动调整原始JSON的格式(比如缩进、键的排序、多余空格的处理)
  • JSON.stringify的输出和原始请求体的字节流无法保证完全一致

修复方案

不要依赖解析后的req.body,直接获取原始请求体的字节流来计算签名:

步骤1:配置中间件保留原始请求体(以Express为例)

禁用默认的json中间件,改用raw中间件获取未修改的请求体:

const express = require('express');
const crypto = require('crypto');
const app = express();

// 用raw中间件捕获原始请求体,指定处理application/json类型
app.use(express.raw({ type: 'application/json' }));

步骤2:修改签名验证逻辑

直接使用原始请求体Buffer计算HMAC,避免JSON序列化带来的差异:

const secret = "SUPERSECRETKEY";

app.post('/webhook', (req, res) => {
    const sig = req.headers['sig'];
    // 直接使用req.body(此时为原始Buffer)
    const hash = "sha256=" + crypto.createHmac('sha256', secret)
        .update(req.body)
        .digest('hex');

    if (hash === sig) {
        // 若需处理请求数据,可在此手动解析原始Buffer
        const bodyData = JSON.parse(req.body.toString());
        res.status(200).send("OK");
    } else {
        res.status(401).send("Unauthorized");
    }
});

app.listen(3000);

额外注意事项

  • 确保请求的Content-Type为application/json,否则raw中间件无法正确捕获请求体
  • 确认签名格式完全匹配:PHP和JS生成的十六进制哈希均为小写,需保证请求头的sig前缀(sha256=)和哈希部分大小写一致

内容的提问来源于stack exchange,提问作者Marius

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 23:45:43