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

Node.js Crypto.js处理阿拉伯语/带重音字符签名异常问题

解决Node.js Crypto HMAC SHA256跨系统签名不匹配问题

核心问题分析

你的问题出在两个关键点:

  • JSON.stringify(req.body)引入序列化差异:不同系统(或不同JSON解析/序列化实现)对非ASCII字符的转义、对象属性的序列化顺序可能存在细微差别,导致最终用于计算签名的字符串不一致。Mac系统的JSON序列化行为恰好和服务商一致,而Windows/Linux则不同。
  • 编码的隐式处理:虽然Node.js的crypto.update默认使用UTF-8,但如果请求体解析或字符串序列化过程中编码被篡改,也会导致签名不匹配。

解决方案

方案1:使用原始请求体Buffer计算签名(推荐)

服务商大概率是直接基于原始HTTP请求体的二进制数据计算签名,而非解析后再序列化的JSON字符串。因此直接使用原始请求体Buffer计算,能完全避免序列化差异问题。

  1. 配置Express获取原始请求体:
import express from 'express';
const app = express();

// 先添加raw中间件捕获原始请求体(Buffer类型)
app.use(express.raw({ type: 'application/json' }));

// 再解析JSON到req.body(如果业务逻辑需要使用解析后的对象)
app.use(express.json());
  1. 修改签名验证逻辑:
import crypto from "crypto";
const secret = "secret";

const validateHeaders = (req, res, next) => {
  // 使用原始请求体Buffer计算签名
  const hash = crypto
    .createHmac("sha256", secret)
    .update(req.body) // 此时req.body是raw中间件提供的原始Buffer
    .digest("hex");
  
  if (hash === req.headers["_signature"]) {
    return next();
  }
  return res.sendStatus(401);
};

方案2:强制统一JSON序列化行为(若无法使用原始请求体)

如果必须基于解析后的req.body对象计算,需确保JSON序列化行为和服务商完全一致:

  1. 显式指定JSON.stringify的参数,消除格式差异:
// 使用无缩进、无空格的序列化,保留原始UTF-8字符(不转义为Unicode序列)
const serializedBody = JSON.stringify(req.body, null, 0);
  1. 显式指定crypto.update的编码为UTF-8:
const hash = crypto
  .createHmac("sha256", secret)
  .update(serializedBody, 'utf8')
  .digest("hex");
  1. 确保请求体解析时使用UTF-8编码:
app.use(express.json({ encoding: 'utf8' }));

验证要点

  • 对比你生成的序列化字符串和服务商使用的字符串是否完全一致(包括字符编码、空格、换行、属性顺序)
  • 测试非ASCII字符时,直接打印serializedBody或原始Buffer的UTF-8字符串,确认和服务商的原始请求体内容完全匹配

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 01:23:35