将PHP POST响应的JSON主体编码为HMAC SHA256,然后编码为Base64。

3

如何从HTTP POST webhook接收原始JSON响应?


我正在使用API,并且为了验证webhook的POST确实来自适当的公司API,他们建议使用以下方法:

为了让客户端验证webhook消息确实来自SIGNIFYD,每个webhook POST消息中都包含一个X-SIGNIFYD-SEC-HMAC-SHA256头。 此头的内容是消息JSON主体的HMAC SHA256编码的Base64编码输出,使用团队的API密钥作为加密密钥。 要验证webhook消息的真实性,您应该自己计算此值并验证它是否等于头中包含的值。

对于测试环境,“secret”密钥是ABCDE而不是“Team API key”。

我在PHP中这样接收它:

<?php

// Get relevant Signifyd custom headers to be used for verification
$header_sig_topic = $_SERVER['HTTP_X_SIGNIFYD_TOPIC'];
$header_sig_sec_hmac = $_SERVER['HTTP_X_SIGNIFYD_SEC_HMAC_SHA256'];

// Get POST body
$webhookContent = "";
$webhook = fopen('php://input' , 'r');
while (!feof($webhook)) {
    $webhookContent .= fread($webhook, 4096);
}
fclose($webhook);

?>

然后我将其处理成哈希值,如下所示:
<?php
$sig_ver_sha = hash_hmac('sha256', $webhookContent, $secret);
$sig_ver_hash = base64_encode( $sig_ver_sha );
?>

然而,我做错了什么,在这个例子中计算出来的哈希值是

OTc1YzExZDY2ZTE1MTVmYmJmNWNhNDRhNWMxZGIzZDk0NmM3OGE4NDU2N2JkYTJmZDJlYWI0ODRhNjlhNTdiYg==

然而,与之完全相同的样本响应头部的哈希为

W+D70ded8u5DG7P4BcG0u2etvAqQZvxz70Q4OXh0vlY=

我以为我的JSON体有问题,所以尝试了每种json_encode和json_decode的组合,但没有任何帮助,我的哈希从未匹配。

我还尝试使用$webhookContent = json_decode(file_get_contents('php://input'), true);来存储POST体,但那只是返回空值($_POST也不起作用)。

除了接收JSON,我还做错了些什么吗?


JSON是测试响应的主体,总是伴随着W+D70ded8u5DG7P4BcG0u2etvAqQZvxz70Q4OXh0vlY=作为用于验证的哈希密钥:

{"analysisUrl": "https://signifyd.com/v2/cases/1/analysis", "entriesUrl": "https://signifyd.com/v2/cases/1/entries", "notesUrl": "https://signifyd.com/v2/cases/1/notes", "orderUrl": "https://signifyd.com/v2/cases/1/order", "guaranteeEligible":false, "status":"DISMISSED", "uuid":"709b9107-eda0-4cdd-bdac-a82f51a8a3f3", "headline":"John Smith", "reviewDisposition":null, "associatedTeam":{ "teamName":"anyTeam", "teamId":26, "getAutoDismiss":true, "getTeamDismissalDays":2 }, "orderId":"19418", "orderDate":"2013-06-17T06:20:47-0700", "orderAmount":365.99, "createdAt":"2013-11-05T14:23:26-0800", "updatedAt":"2013-11-05T14:23:26-0800", "adjustedScore":262.6666666666667, "investigationId":1, "score":262.6666666666667, "caseId":1, "guaranteeDisposition":"APPROVED"}

如果有助于看出我做错了什么,提供了一个例子,但它是用Python编写的:

Mac sha256HMAC = javax.crypto.Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(teamAPIKEY.getBytes(), "HmacSHA256");
sha256HMAC.init(secretKey);
String encodedHMAC256 = Base64.encodeBase64String(sha256HMAC.doFinal(jsonBody.getBytes("UTF-8")));

没有加密密钥in sha256(),我建议查找sha256()base64_encode() - ArtisticPhoenix
但是指令建议“对消息的JSON正文进行HMAC SHA256编码,使用团队的API密钥作为加密密钥” - 而PHP文档则说hash_hmac %20 string $algo , string $data , string $key , $raw_output = false %29 。我是否理解错误?你能详细说明一下吗? - Andre Bulatov
在PHP中,$webhookContent是JSON格式还是数组? - ArtisticPhoenix
1
好的,我可以告诉你,普通的sha256哈希值是64个字符,但是你的base64哈希值只有44个字符,所以你尝试过将hash_hmac函数的第四个参数设置为true,以获取原始二进制输出吗? - ArtisticPhoenix
1
请参阅此链接http://php.net/manual/en/function.hash-hmac.php,特别是“要对Amazon AWS查询进行签名,请对二进制值进行base64编码:”。 - ArtisticPhoenix
很棒,谢谢!正是因为那个 true 才能实现它。如果没有 true,那么输出就不是原始的了,那它是什么呢?PS:我应该回答并接受还是你会撰写答案,然后我接受? - Andre Bulatov
1个回答

0

我的错误在于简单地没有将hash_hmac()函数的$raw_output参数指定为true

raw_output
当设置为TRUE时,输出原始二进制数据。FALSE则输出小写十六进制数。

因此,由于我没有将$raw_output指定为true,我得到的是十六进制数而不是原始二进制输出,看起来像这样:975c11d66e1515fbbf5ca44a5c1db3d946c78a84567bda2fd2eab484a69a57bb

编辑:答案在这里。

<?php
$sig_ver_sha = hash_hmac('sha256', $webhookContent, $secret, true);
$sig_ver_hash = base64_encode( $sig_ver_sha );
?>

嘿@Andre,我在这里做错了什么?$check = base64_encode(hash_hmac('sha256', $request->getContent(), config('services.signifyd.key'), true)); Log::info("Check: " . $check); - JoshySav
不确定。看起来正确。你确定所有部分都正确吗?在编码之前尝试记录单个部分。另外,请确保您的密钥是正确的。 - Andre Bulatov
感谢@Andre!所以我正在使用Postman进行测试,JSON主体的格式意味着\n字符被编码并使哈希无效。感谢您抽出时间回复。 - JoshySav

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接