当客户端安装应用程序时,他们可以选择在/admin/apps
页面上的应用程序列表中单击应用程序名称。
当他们单击该页面时,我的应用程序的PHP索引文件会收到以下$_GET
变量:
hmac = some_long_alphanumaeric_hmac
locale = en
protocol = https://
shop = example-shop.myshopify.com
timestamp = 1535609063
要验证来自Shopify的Webhook,我可以成功使用此功能:
function verify_webhook($data, $hmac_header, $app_api_secret) {
$calculated_hmac = base64_encode(hash_hmac('sha256', $data, $app_api_secret, true));
return ($hmac_header == $calculated_hmac);
}
// Set vars for Shopify webhook verification
$hmac_header = $_SERVER['HTTP_X_SHOPIFY_HMAC_SHA256'];
$data = file_get_contents('php://input');
$verified = verify_webhook($data, $hmac_header, MY_APP_API_SECRET);
是否可以验证来自安装了该应用程序的Shopify客户端的应用程序管理员页面访问?
PS:我已经仔细研究了Embedded Apps API和{{3}两者(但我不知道这是否是正确的文档,或者我做错了什么)。 }(其中没有有关如何验证嵌入式应用程序管理员页面访问的说明)。
我尝试了其他各种方法,在此过程中发现了一些荒谬的问题,但还是没有运气。
应使用我理解的方法来验证Shopify HMAC是否类似于此:
function verify_hmac($hmac = NULL, $shopify_app_api_secret) {
$params_array = array();
$hmac = $hmac ? $hmac : $_GET['hmac'];
unset($_GET['hmac']);
foreach($_GET as $key => $value){
$key = str_replace("%","%25",$key);
$key = str_replace("&","%26",$key);
$key = str_replace("=","%3D",$key);
$value = str_replace("%","%25",$value);
$value = str_replace("&","%26",$value);
$params_array[] = $key . "=" . $value;
}
$params_string = join('&', $params_array);
$computed_hmac = hash_hmac('sha256', $params_string, $shopify_app_api_secret);
return hash_equals($hmac, $computed_hmac);
}
但是行$params_string = join('&', $params_array);
通过将×tamp
编码为xtamp
导致了一个令人讨厌的问题……使用http_build_query($params_array)
会导致同样荒谬的事情。发现其他人有同样的问题GitHub example provided。基本上可以通过将&
编码为&
来解析为$params_string = join('&', $params_array);
。
我的最终版本是这样的,但仍然无法正常工作(所有注释的代码是我尝试的其他无效代码):
function verify_hmac($hmac = NULL, $shopify_app_api_secret) {
$params_array = array();
$hmac = $hmac ? $hmac : $_GET['hmac'];
unset($_GET['hmac']);
// unset($_GET['protocol']);
// unset($_GET['locale']);
foreach($_GET as $key => $value){
$key = str_replace("%","%25",$key);
$key = str_replace("&","%26",$key);
$key = str_replace("=","%3D",$key);
$value = str_replace("%","%25",$value);
$value = str_replace("&","%26",$value);
$params_array[] = $key . "=" . $value;
// This commented out method below was an attempt to see if
// the imporperly encoded query param characters were causing issues
/*
if (!isset($params_string) || empty($params_string)) {
$params_string = $key . "=" . $value;
}
else {
$params_string = $params_string . "&" . $key . "=" . $value;
}
*/
}
// $params_string = join('&', $params_array);
// echo $params_string;
// $computed_hmac = base64_encode(hash_hmac('sha256', $params_string, $shopify_app_api_secret, true));
// $computed_hmac = base64_encode(hash_hmac('sha256', $params_string, $shopify_app_api_secret, false));
// $computed_hmac = hash_hmac('sha256', $params_string, $shopify_app_api_secret, false);
// $computed_hmac = hash_hmac('sha256', $params_string, $shopify_app_api_secret, true);
$computed_hmac = hash_hmac('sha256', http_build_query($params_array), $shopify_app_api_secret);
return hash_equals($hmac, $computed_hmac);
}
答案 0 :(得分:0)
如果您从Shopify获得成功,那么您要做的第一件事就是在持久层中检查是否已注册商店。如果您这样做了,并且进行了某种形式的设置,则可以自由地将应用程序呈现给该商店。如果您没有让商店持续存在,那么请经历oAuth周期,以获取要在商店上使用的身份验证令牌,该令牌将与商店和新会话一起保留。
对于商店中要接收Webhook的任何路线或终点,这些请求当然都没有会话,因此您可以使用HMAC安全方法来确定要做什么。因此,您的问题显然是跨越两个不同的概念,每个概念的处理方式都不相同。有关差异的文档非常清楚。
答案 1 :(得分:0)
以下是相关文档:https://shopify.dev/tutorials/authenticate-with-oauth#verification。 Sandeep的此信息也非常有用:https://community.shopify.com/c/Shopify-APIs-SDKs/HMAC-verify-app-install-request-using-php/m-p/140097#comment-253000。
这对我有用:
function verify_visiter() // returns true or false
{
// check that timestamp is recent to ensure that this is not a 'replay' of a request that has been intercepted previously (man in the middle attack)
if (!isset($_GET['timestamp'])) return false;
$seconds_in_a_day = 24 * 60 * 60;
$older_than_a_day = $_GET['timestamp'] < (time() - $seconds_in_a_day);
if ($older_than_a_day) return false;
$shared_secret = Your_Shopify_app_shared_secret;
$hmac_header = $_GET['hmac'];
unset($_GET['hmac']);
$data = urldecode(http_build_query($_GET));
$calculated_hmac = hash_hmac('sha256', $data, $shared_secret, false);
return hash_equals($hmac_header, $calculated_hmac);
}
$verified = verify_visiter();
if (!$verified) {
exit('User verification failed.');
}
// ... everything else...
答案 2 :(得分:-1)
public function authenticateCalls($data = NULL, $bypassTimeCheck = FALSE)
{
$da = array();
foreach($data as $key => $val)
{
$da[$key] = $val;
}
if(isset($da['hmac']))
{
unset($da['hmac']);
}
ksort($da);
// Timestamp check; 1 hour tolerance
if (!$bypassTimeCheck)
{
if (($da['timestamp'] - time() > 3600))
{
return false;
}
}
// HMAC Validation
$queryString = http_build_query($da);
$match = $data['hmac'];
$calculated = hash_hmac('sha256', $queryString, $this->_API['API_SECRET']);
return $calculated === $match;
}