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

Stripe Webhook签名验证失败:verifyWebhookSignature返回false

Stripe Webhook签名验证失败:crypto.timingSafeEqual始终返回false的排查方案

以下是核心排查方向和修复建议,结合你的代码逐一分析:


1. 原始请求体(Payload)被修改

Stripe签名验证依赖完全未修改的原始请求体字节流,任何中间件的自动解析、格式化、转码都会导致签名不匹配:

  • 确认ctx.request.body[unparsed]是原始的UTF-8编码字符串,而非Buffer或已解析的JSON对象。如果是Buffer,需先转成UTF-8字符串:
    const rawPayload = ctx.request.body[unparsed].toString('utf8');
    
  • 对比Stripe Dashboard「Events」中该Webhook事件的原始Payload,和你代码中打印的signedPayload里的payload部分,确保完全一致(包括空格、换行、引号格式等)。比如框架自动给JSON键值对添加空格({"a":1}变成{"a": 1}),会直接导致签名失效。

2. 签名头(stripe-signature)解析错误

你的签名头分割逻辑可能忽略了元素前后的空格:
Stripe签名头格式类似t=1699999999, v0=abcdef...,split(',')后第二个元素会带前导空格,导致key变成' v0'而非'v0',最终无法正确提取签名值。修改解析逻辑:

for (const element of elements) {
  const trimmedElement = element.trim(); // 去掉前后空格
  const [key, value] = trimmedElement.split('=');
  if (key === 't') {
    timestamp = parseInt(value, 10);
  } else if (key.startsWith('v0')) {
    signatures['v0'] = value;
  }
}

3. 时间戳与Payload拼接问题

确保拼接signedPayload时没有额外字符:

  • 确认timestamp是整数转成的字符串,无前置零或格式错误。
  • 拼接时直接用${timestamp}.${payload},不要添加任何空格或换行符。

4. Endpoint Secret正确性验证

再次确认:

  • 使用的是Stripe Dashboard中对应Webhook端点的Signing Secret(不是API Key)。
  • 测试环境和生产环境的Secret完全不同,不要混用。

5. Buffer转换与长度检查

crypto.timingSafeEqual要求两个Buffer长度完全一致,否则会返回false。在对比前添加长度检查:

const expectedBuf = Buffer.from(expectedSignature, 'hex');
const actualBuf = Buffer.from(signatures['v0'], 'hex');
if (expectedBuf.length !== actualBuf.length) {
  console.error('Signature length mismatch');
  return false;
}
return crypto.timingSafeEqual(expectedBuf, actualBuf);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 01:40:03