我正在使用 gocardless hooks ,并且我一直坚持使用沙箱环境验证 hmac 签名。
因此,我在沙箱中生成了 webhook 测试,它向我提供了已发送的请求正文和标头。
据我了解,我必须获取请求正文并生成带有密钥的哈希值,并将其与 webhook 签名标头哈希值进行比较(听起来并不复杂)。
所以我使用邮递员在我的本地环境中重现它,附加相同的标头和相同的正文,但签名永远不匹配。
这是我尝试过的:
$signature = hash_hmac('sha256',$request->getContent(), 'secret');
注意我正在使用 laravel 框架,所以我的第一个想法是框架可能正在内部操纵请求,所以我尝试了这个:
$request_data = file_get_contents('php://input');
$signature = hash_hmac('sha256',$request_data, 'secret');
但仍然不匹配,我注意到有一些新行,也许这可以修改结果,所以我清理了它......
$request_data = str_replace(array('.', ' ', "\n", "\t", "\r"), '', $request_data);
但仍然不匹配,而且我还尝试将正文数据转换为 utf8 ,并使 hmac 返回原始数据并将其编码为 base64 ...但没有成功。
那么这里可能出了什么问题呢?也许签名在沙箱环境中不起作用?有人处理过吗
提前致谢!
最后我发现了问题,在goocardless的沙盒面板中他们显示了请求unminified,所以长度不同,只需缩小它就可以测试了!现在签名匹配了!
我们更新了开发人员文档,您可以在 https://developer.gocardless.com/getting-started/api/staying-up-to-date-with-webhooks/? 找到验证 webhook 签名的示例lang=php#building-a-webhook-handler
你想做类似的事情
<?php
// We recommend storing your webhook endpoint secret in an environment variable
// for security, but you could include it as a string directly in your code
$token = getenv("GC_WEBHOOK_SECRET");
$raw_payload = file_get_contents('php://input');
$headers = getallheaders();
$provided_signature = $headers["Webhook-Signature"];
$calculated_signature = hash_hmac("sha256", $raw_payload, $token);
if ($provided_signature == $calculated_signature) {
// Process the events
header("HTTP/1.1 200 OK");
} else {
header("HTTP/1.1 498 Invalid Token");
}
如果你遇到这个问题,这意味着 laravel 已经更改了传入的请求正文,因此你需要将其恢复为正常形式,如下所示
$request_data = json_decode($request->getContent(), true);
$corrected_request_json = json_encode($request_data, JSON_UNESCAPED_SLASHES);
$calculated_signature = hash_hmac("sha256", $corrected_request_json, $token);