为什么 Stripe-Signature 标头永远不会与 request.body 的签名匹配?

Sea*_*ean 4 python django stripe-payments django-rest-framework

我正在使用 Python 和 Django Rest 框架,并尝试从 stripe 正确接收 webhook 事件。

但是我不断收到此错误:

stripe.error.SignatureVerificationError:找不到与有效负载的预期签名匹配的签名

这是代码:

WEBHOOK_SECRET = settings.STRIPE_WEBHOOK_SK

@csrf_exempt
def webhook(request):
    sig_header = request.headers.get('Stripe-Signature', None)
    payload = request.body
    try:
        event = stripe.Webhook.construct_event(
            payload=payload, 
            sig_header=sig_header, 
            secret=WEBHOOK_SECRET
        )
    except ValueError as e:
        raise e
    except stripe.error.SignatureVerificationError as e:
        raise e

    return HttpResponse(status=200)
Run Code Online (Sandbox Code Playgroud)

我还尝试修改请求正文格式,如下所示:

payload = request.body.decode('utf-8')
# and also
payload = json.loads(request.body)
Run Code Online (Sandbox Code Playgroud)

但还是没有运气。

该错误来自类verify_header()内部的类方法WebhookSignature

这是该方法失败的部分:

if not any(util.secure_compare(expected_sig, s) for s in signatures):
    raise error.SignatureVerificationError(
        "No signatures found matching the expected signature for payload",
        header,
        payload,
    )
Run Code Online (Sandbox Code Playgroud)

所以我在这一行之前打印出来exptected_sigsignatures发现无论采用什么格式request.bodysignatures总是存在(这很好),但它们永远不会与标头中的签名匹配。

为什么是这样?

koo*_*jah 6

当 Stripe 计算发送给您的事件的签名时,它使用代表整个事件内容的特定“有效负载”。签名是在该确切的有效负载上完成的,对其进行的任何更改(例如添加新行、删除空格或更改属性的顺序)都会更改有效负载和相应的签名。

当您验证签名时,您需要确保传递 Stripe 发送给您的确切原始负载,否则您计算的签名将与 Stripe 不匹配。

框架有时会在收到请求时尝试提供帮助,它们会检测 JSON 并自动为您解析它。这意味着您认为您正在获得“原始有效负载/主体”,但实际上您获得了替代版本。它具有相同的内容,但与 Stripe 发送给您的内容不匹配。

例如,这在 Node.js 中的 Express 中相当常见。因此,作为开发人员,您必须明确请求 Stripe 发送给您的确切原始/原始有效负载。如何做到这一点可能因多种因素而异。stripe-node github 上有 2 个问题,这里这里有许多潜在的修复方案。

对于 Django,同样的情况也可能发生,您需要确保您的代码请求原始有效负载。您似乎按预期使用request.body,但这是您想要进一步深入研究的一件事。

此外,另一个常见错误是使用错误的 Webhook 密钥。例如,如果您使用 Stripe CLI,它会为您创建一个密钥,该密钥与您在此 Webhook 端点的仪表板中看到的密钥不同。您需要确保根据您所处的环境使用正确的密钥。