【问题标题】:Stripe webhook signature failed - Stripe.netStripe webhook 签名失败 - Stripe.net
【发布时间】:2017-10-08 20:59:20
【问题描述】:

我正在尝试使用 Jayme Davis 的 c# 库 Stripe.net 实现条带 webhook。我已经在条带仪表板中设置了测试端点并生成了秘密。端点被很好地击中,并将使用 StripeEventUtility.ParseEvent 生成 StripeEvent。问题在于使用 ConstructEvent 函数我无法让签名匹配。任何帮助或建议将不胜感激。

isSignaturePresent 返回错误

//call to create event
stripeEvent = ConstructEvent(json, Request.Headers["Stripe-Signature"], 
SecretFromStripeDashBoard);


private StripeEvent ConstructEvent(string json, string 
stripeSignatureHeader, string secret, int tolerance = 300)
    {
        var signatureItems = parseStripeSignature(stripeSignatureHeader);

        var signature = computeSignature(secret, signatureItems["t"].FirstOrDefault(), json);

        if (!isSignaturePresent(signature, signatureItems["v1"]))
            throw new Exception("The signature for the webhook is not present in the Stripe-Signature header.");

        //var utcNow = EpochUtcNowOverride ?? DateTime.UtcNow.ConvertDateTimeToEpoch();
        //var webhookUtc = Convert.ToInt32(signatureItems["t"].FirstOrDefault());

        //if (utcNow - webhookUtc > tolerance)
        //    throw new Exception("The webhook cannot be processed because the current timestamp is above the allowed tolerance.");

        return Mapper<StripeEvent>.MapFromJson(json);
    }

    private ILookup<string, string> parseStripeSignature(string stripeSignatureHeader)
    {
        return stripeSignatureHeader.Trim()
            .Split(',')
            .Select(item => item.Trim().Split('='))
            .ToLookup(item => item[0], item => item[1]);
    }

    private bool isSignaturePresent(string signature, IEnumerable<string> signatures)
    {
        return signatures.Any(key => secureCompare(key, signature));
    }

    private string computeSignature(string secret, string timestamp, string payload)
    {
        var secretBytes = Encoding.UTF8.GetBytes(secret);
        var payloadBytes = Encoding.UTF8.GetBytes($"{timestamp}.{payload}");

        var cryptographer = new HMACSHA256(secretBytes);
        var hash = cryptographer.ComputeHash(payloadBytes);

        return BitConverter.ToString(hash).Replace("-", "").ToLower();
    }

    private bool secureCompare(string a, string b)
    {
        if (a.Length != b.Length) return false;

        var result = 0;

        for (var i = 0; i < a.Length; i++)
        {
            result |= a[i] ^ b[i];
        }

        return result == 0;
    }
}

【问题讨论】:

  • 如何初始化json 变量?
  • 我使用 var json = JsonSerializer.SerializeToString(request);
  • 所以您正在重新序列化反序列化的请求正文。但是,这不太可能返回与原始请求正文完全相同的字符串。您需要使用 Web 服务器/框架传递的原始请求正文。
  • 会试一试,谢谢
  • 是的,这是一种享受,谢谢。

标签: c# stripe-payments digital-signature webhooks stripe.net


【解决方案1】:

我在上面的 cmets 中进行了回答,但回顾一下,问题是提供给 ConstructEvent 方法的 json 字符串不包含 Stripe 发送的确切有效负载。

相反,您使用以下方式初始化了有效负载:

var json = JsonSerializer.SerializeToString(request);

即您重新序列化了反序列化请求的正文。但是,您不太可能获得与 Stripe 发送的原始有效负载相同的字符串。例如。 Stripe 可以发送此有效载荷:

{
  "a_key": "a_value",
  "another_key": "another_value"
}

但是在反序列化+重新序列化之后,你的 JSON 字符串可能包含这个值:

{"another_key": "another_value","a_key": "a_value"}

因为不能保证保持键顺序,并且其他格式选项(换行符、缩进)也会发挥作用。

Webhook 签名是使用 exact 有效负载(作为原始字符串)生成的,因此在验证签名时,您还必须提供由您的 Web 服务器或框架传递的确切有效负载。

【讨论】:

  • 嗨@Ywain,如何从请求正文中获取确切的有效负载。我尝试了几种方法(将正文作为 [FromBody] 获取正文,从 StreamReader 获取正文等),但都没有解决问题。
  • 嗨@IsuruSiriwardana。如果您使用 ASP.NET,您应该能够通过以下方式获取原始正文:string rawBody; using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8)) { rawBody = await reader.ReadToEndAsync(); }
【解决方案2】:

如上一个答案中所指出的,json 字符串应该与 stripe 发送的播放负载相同。在我的情况下,我必须删除 \r 才能使其像关注 var json = (await stripStream.ReadToEndAsync()).Replace("\r", ""); 一样工作 代码如下:

 [HttpPost("webhook")]
    public async Task<IActionResult> Index()
    {
        try
        {
            using (var stripStream = new StreamReader(HttpContext.Request.Body))
            {
                var json = (await stripStream.ReadToEndAsync()).Replace("\r", "");
                var secretKey = _settings.Value.StripeSettings.WebhookSecretKey;
                // validate webhook called by stripe only
                Logger.Information($"[WEBHOOK]: Validating webhook signature {secretKey.Substring(0, 4)}**********");
                var stripeEvent = EventUtility.ConstructEvent(json, Request.Headers["Stripe-Signature"], secretKey);
                Logger.Information($"[WEBHOOK]: Webhook signature validated");

                // handle stripe event
                await this._stripeWebhookService.HandleAsync(stripeEvent);

                return Ok();
            }
        }catch(Exception){ // handle exception}

【讨论】:

    【解决方案3】:

    此时我的应用程序也不断抛出异常。

     var stripeEvent = EventUtility.ConstructEvent(
                  json,
                  Request.Headers["Stripe-Signature"],
                  secret
                );
    

    阅读异常日志,它说我使用的是新版本的 NuGet 包 stripe.net 和条带仪表板中的旧版本。我将我的帐户更新到最新的 API 版本,并且不再收到错误消息。

    如果您在其他应用程序中使用旧版本,请小心更新仪表板中的版本。如果仪表板中的更新破坏了您的其他应用程序,您有 72 小时的时间撤消该更新。我是从零开始,所以更新对我来说没什么大不了的。

    【讨论】:

      猜你喜欢
      • 2021-11-28
      • 1970-01-01
      • 1970-01-01
      • 2021-09-18
      • 2018-03-09
      • 2022-12-12
      • 2012-08-25
      • 2023-03-08
      • 2021-12-24
      相关资源
      最近更新 更多