ShareFile API SHA-256 HMAC digest - PHP/Laravel

  • 1
  • Question
  • Updated 11 months ago
  • (Edited)
How to properly use hash_hmac in php to generate a sha-256 hmac used to verify the H parameter returned during the authentication workflow.

I need to know what the process is, I have followed the process outlined in the sharefile API to complete this task but I am always left with an incorrect hmac.

Here are the below steps from the API:

  1. Remove the "h" query string parameter from your request URI.
  2. Parse out just the path and remaining query string parameters. For example, if the request uri is: https://www.myapp.com/oauth?code=c123&subdomain=mycompany&apicp=sharefile.com&appcp=sharefile.com&h=abcdef1234 then the path and query string you will be validating will be /oauth?code=c123&subdomain=mycompany&apicp=sharefile.com&appcp=sharefile.com
  3. Convert the path and query string to an array of bytes using UTF-8 encoding.
  4. Create a digest of the array of bytes using HMAC SHA-256, using your client secret.
  5. Convert the digest to text using base 64 encoding.
  6. URL encode the text and compare to the value in the "h" query string parameter. If they match, then you can trust the data was not tampered with. If they don't match you should ignore the values and re-authenticate

So I am doing these as stated, laravel has in built functions for gathering only the url path and $request->except('h'); gets me all the params except h. I have tried parsing this to utf 8, array of btes, hex etc and then using hash_hmac with my client secret and tried setting raw data to true and false on that, base64 encoding it after the fact etc can't get it to work. Whats the proper process?

Photo of Jacob

Jacob

  • 5 Posts
  • 1 Reply Like
  • frustrated

Posted 11 months ago

  • 1
Photo of Dale Smith

Dale Smith, Software Engineer

  • 187 Posts
  • 26 Reply Likes
Hi Jacob,

I'm afraid I'm not super familiar with PHP, but based on the manpage (http://php.net/manual/en/function.hash-hmac.php) here's what I would expect:

Assuming your url receiving the authorization code is:

https://www.myapp.com/sharefilecallback?code=c123&subdomain=mycompany&apicp=sharefile.com&appcp=sharefile.com&h=abcdef123456

Based on this stack overflow (https://stackoverflow.com/questions/34957002/hmac-sha-256-in-php)


You would call it like:
function encode($data) {
    return str_replace(['+', '/'], ['-', '_'], base64_encode($data));
}

function decode($data) {
    return base64_decode(str_replace(['-', '_'], ['+', '/'], $data));
}

$binaryKey = decode($key);

var_dump(encode(hash_hmac("sha256", $str, $binaryKey, true)));
Where:
$str is /sharefilecallback?code=c123&subdomain=mycompany&apicp=sharefile.com&appcp=sharefile.com
and 
$key is your client secret.

Hope this helps,
Dale
Photo of Jacob

Jacob

  • 5 Posts
  • 1 Reply Like
Hey Dale,

I appreciate the help. However I still seem to be having the same issue. The code above does not produce and exact match for the H param thats returned. One question would be if you include the state param in the login process (its an optional param) would that need to be stripped as well or factored into the equation?

I just can't seem to get this parsed properly to match the h param. I know in php/laravel you don't need to convert a string into a byte array and the hash_hmac function expects a string as opposed to a byte array. I utilized your code to a T and still am getting hashes that do not match.

Any further help would be appreciated!
Photo of Dale Smith

Dale Smith, Software Engineer

  • 187 Posts
  • 26 Reply Likes
Hey Jacob,

Yes if the state param is returned, you'd also need to include that. The hash is a hash of the entirety of the path and query string (minus the actual h parameter itself. It's best to take the raw request uri, remove the &h=hash from the end and remove the domain from the start of the string. 

Let me know if that helps.
Photo of Jacob

Jacob

  • 5 Posts
  • 1 Reply Like
So I found the issue and maybe this will help people using laravel in the future. Laravel re-arranges query parameters in what appears to be alphabetical order when you use $request->getQueryString() and as a result the hashing never matches up.

Beyond that, laravel decodes urls by default which also interferes with verifying the hashes as well. This played a part in eventually properly decoding my state parameter

What I ended up doing was using something like this:

$params = $request->all();  or can be $params = Request::all(); etc

writing a foreach loop with key, val and verifying param wasn't 'h' and concatenating a string of the params. When using the all() portion laravel doesn't re-arrange the params but is an associative array.

I also was able to get base path without issues using $request->getPathInfo();

with this combined I was able to verify the hash and I am now good to go.

Thanks again dale for the help, have a good day.
Photo of Dale Smith

Dale Smith, Software Engineer

  • 187 Posts
  • 26 Reply Likes
Jacob,

Good find! Thanks for sharing that. I'm sure it will come in handy for other Laravel users.

Thanks,
Dale