verifyWebhookSignature 返回 false

问题描述 投票:0回答:1
api.use(async (ctx, next) => {
    if (ctx.path === '/webhook') {
        const sig = ctx.request.headers['stripe-signature'];
        unparsed = Symbol.for('unparsedBody')
        try {
            event = stripe.webhooks.constructEvent(ctx.request.body[unparsed], sig, endpointSecret);
        }
        catch (err) {
            console.log("error", err)
        }

构造事件函数报告此错误: StripeSignatureVerificationError:未找到与有效负载的预期签名匹配的签名。

所以我确信endpointSecret是正确的,我也传递了unparsedBody。所以我猜测这是签名错误,所以我使用这个函数来验证签名:

function verifyWebhookSignature(signatureHeader, payload, secret) {
    try {
      // Step 1: Extract the timestamp and signatures
      const elements = signatureHeader.split(',');
      let timestamp = null;
      const signatures = {};
      for (const element of elements) {
        const [key, value] = element.split('=');
        if (key === 't') {
          timestamp = parseInt(value, 10);
        } else if (key.startsWith('v0')) {
          signatures['v0'] = value;
        }
      }
  
      // If there's no timestamp or signature, or if the timestamp is too old, return false.
      console.log("timestamp: ", timestamp)
      if (!timestamp || (Date.now() / 1000 - timestamp) > 300) {
        return false;
      }

      // Step 2: Prepare the signed_payload string
      const signedPayload = `${timestamp}.${payload}`;
  
      console.log("signedPayload: ", signedPayload)

      // Step 3: Determine the expected signature
      const expectedSignature = crypto
        .createHmac('sha256', secret)
        .update(signedPayload)
        .digest('hex');
  
    console.log("expectedSignature: ", expectedSignature)   
    console.log("signatures[v0]: ", signatures['v0'])
      // Step 4: Compare the signatures
      if (signatures['v0']) {
        console.log(100)
        return crypto.timingSafeEqual(
          Buffer.from(expectedSignature, 'hex'),
          Buffer.from(signatures['v0'], 'hex')
        );
      } else {
        console.log(888)
        return false;
      }
    } catch (error) {
        console.log(999)
      // Handle any exceptions or errors
      console.error(`Signature verification failed: ${error}`);
      return false;
    }
  }

原来这行:

        return crypto.timingSafeEqual(
          Buffer.from(expectedSignature, 'hex'),
          Buffer.from(signatures['v0'], 'hex')
        );

始终返回 false。

我打印了大部分数据,如果您需要任何额外的数据,请随时告诉我。

有人知道为什么吗?非常感谢!

node.js stripe-payments koa
1个回答
0
投票

据我所知,签名验证可能在几个不同的地方出错。

这些问题通常是由以下几种情况引起的:

  1. 签名根本不在事件中。我认为情况并非如此,因为您输入了日志行来确认这一点,所以进入下一个
  2. 您代码中的 Webhook 机密与仪表板中的 Webhook 机密不同。您可以通过导航到仪表板的 Webhook 页面并单击您正在使用的 Webhook 端点来仔细检查这一点。从那里您将看到一些可点击的文本,在“签名秘密”下显示“显示”。确认那里的签名密钥与您在代码中使用的密钥相同。您应该有一行看起来像这样的行,您可以在其中复制/粘贴它 -->
    const endpointSecret = 'whsec_...'; 
  3. 事件数据字符串的编码未设置为 UTF-8。 Stripe 将 API 中的所有内容都视为 UTF-8,并且 Stripe 签名的字符串最终与您看到的不同。如果还没有的话,您需要在代码中强制执行编码。
  4. 您签名的数据与Stripe签名的数据不同。通常,这意味着您正在使用一个试图提供帮助并将事件数据解析为 JSON 的框架。因此,当您计算自己的签名时,您会在不完全相同的字符串上执行此操作。例如,属性的顺序可能已更改或缩进可能不同。为了使签名匹配,您需要在与 Stripe 完全相同的原始字符串上进行计算。为此,您需要确保获得 Stripe 发送给您的 HTTP 请求的原始正文,而中间不会受到您的代码或框架的任何干扰。
© www.soinside.com 2019 - 2024. All rights reserved.