Verify App Proxy Signature Request (non-webook) - PHP

We have made an App Proxy and data is being sent to it successfully utilizing an Ajax request.

When I look at the data being sent it shows as a $_GET request:

shop => mydomain.myshopify.com
logged_in_customer_id => 
path_prefix => /apps/share
timestamp => 1668542761
signature => e4b381234fdsaf23rt2fwdsaf23f32fsdb531eda4c4ce81057d60015a742d

I cannot seem to Verify the signature against the client secret.

I am using PHP and have tried many different ways:

function verify_webhook($data, $hmac_header)
{

  $ar= [];
  $hmac = $data['signature'];
  unset($data['signature']);

  foreach($data 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);

    $ar[] = $key."=".$value;
  }

  $str = join('&',$ar);
  $ver_hmac =  hash_hmac('sha256',$str,SHOPIFY_APP_SECRET,false);
	
  return($ver_hmac==$hmac);
	
}

Also initially tried this as well:

function verify_webhook($data, $hmac_header)
{

      $calculated_hmac = base64_encode(hash_hmac('sha256', $data, SHOPIFY_APP_SECRET, true));
      return ($hmac_header == $calculated_hmac);
}

Not sure what I am missing.

For the $data am I just sending everything in the $_GET request?

Figured out how to get this verified when there is a hmac query parameter, however I still can’t get this working when it is not present and a signature is in the query parameters.

Needed to remove hmac query param.

// Set variables for our request
$shared_secret = "xxxxxxxxxxxxxxx";
$params = $_GET; // Retrieve all request parameters
$hmac = $_GET['hmac']; // Retrieve HMAC request parameter
$params = array_diff_key($params, array('hmac' => '')); // Remove hmac from params
ksort($params); // Sort params lexographically

// Compute SHA256 digest
$computed_hmac = hash_hmac('sha256', http_build_query($params), $shared_secret);

// Use hmac data to check that the response is from Shopify or not
if (hash_equals($hmac, $computed_hmac)) {
	print 'VALIDATED';
} else {
	print 'NOT VALIDATED';
}

This was figured out. For PHP we must remove the ampersand and replace the %2F:

$params = $_GET;
	$sig = $_GET['signature'];
	$params = array_diff_key($params, array('signature' => '')); // Remove sig from params
	ksort($params); // Sort params lexographically
	$params = str_replace("%2F","/",http_build_query($params));
	$params = str_replace("&","",$params);
	
	// Compute SHA256 digest
	$computed_hmac = hash_hmac('sha256', $params, $shared_secret);

	// Use sig data to check that the response is from Shopify or not
	if (hash_equals($sig, $computed_hmac)) {
		$verified = true;
		
	} else {
		header('HTTP/1.0 401 Unauthorized');
		die;
	}
2 Likes

I try to use the script but doesn’t work. This solution is still correctly ?

This is working properly for Laravel also. The below code worked for Laravel 8.x:

$secret = env('SHOPIFY_API_SECRET');
$params = $request->except(['signature']);
ksort($params);
$params = str_replace("&", "", str_replace("%2F", "/", http_build_query($params)));

// Compute SHA256 digest
$computed_hmac = hash_hmac('sha256', $params, $secret);
return ($computed_hmac == $request->signature) ? $next($request) : response("HTTP/1.0 401 Unauthorized", 401);