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

如何在NodeJS(Fastify)中验证WhatsApp API的X-Hub-Signature-256?

问题分析与解决方案

核心问题

你遇到的签名验证失败,根源在于你使用了解析后再序列化的请求体来计算哈希,而WhatsApp的签名是基于原始请求的JSON字节流生成的。Fastify默认会将JSON请求体解析为JavaScript对象,这个过程中会丢失原始JSON的格式细节(比如键的顺序、空格、转义方式),尤其是媒体消息(音频/图片)的结构更复杂,解析再序列化后的结果和原始请求体差异更大,导致哈希不匹配。

解决方案

直接使用原始请求体的字符串来计算签名,而不是解析后的request.body对象序列化结果。

步骤1:开启Fastify的原始请求体支持

在创建Fastify实例时,开启rawBody选项,让框架保留原始请求体:

import fastify from 'fastify';

const app = fastify({
  rawBody: true, // 开启原始请求体保留
});

步骤2:修改路由中的签名验证逻辑

使用request.rawBody获取原始请求体字符串,用它来计算签名,之后再解析为对象处理业务逻辑:

app.post(
  "/",
  async (
    request: FastifyRequest<{
      Body: WhatsappReceivedMessage;
    }>,
    reply
  ) => {
    const xHubSignature = request.headers["x-hub-signature-256"] as string;
    // 获取原始请求体字符串
    const rawBodyStr = request.rawBody.toString('utf-8');

    if (!xHubSignature) {
      console.log("Missing X-Hub-Signature-256 header");
      return reply.status(403).send("Forbidden");
    }

    // 用原始字符串计算签名
    const expectedSignature = generateWPPSignature(
      rawBodyStr,
      env.WPP_SECRET
    );

    if (!validateWPPSignature(expectedSignature, xHubSignature)) {
      console.warn("Invalid X-Hub-Signature-256");
      return reply.status(403).send("Forbidden");
    }

    // 解析原始字符串为业务对象
    const body = JSON.parse(rawBodyStr) as WhatsappReceivedMessage;
    const message = body.entry[0].changes[0].value.messages[0];

    // 后续业务逻辑(发送消息、标记已读)保持不变...

    return reply.send("OK");
  }
);

步骤3:验证函数无需修改

你的generateWPPSignature和validateWPPSignature函数逻辑是正确的,不需要改动,只要传入原始请求体字符串即可。

关键说明

  • WhatsApp计算签名时,使用的是发送给你的原始JSON请求的字节内容,任何对原始内容的修改(包括解析再序列化、调整空格/键顺序)都会导致哈希不一致。
  • 确保rawBody的编码是utf-8,和WhatsApp发送的请求编码一致,避免字符转义问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 23:41:15