When communicating messages between multiple systems, it’s always a good idea to authenticate a message before taking action on it. This ensures the message hasn’t been tampered with and that it came from a known source. There are several ways of implementing this type of message authentication, but here we’re going to assume we have control over the systems in question and can use a shared secret that all systems know.
Update: as a Redditor pointed out, these implementations may be vulnerable to timing attacks.
Update 2: Using Go’s hmac.Equals function prevents the leakage of time information and is therefore not vulnerable. The Python example has been updated to use a constant-time comparison function as well.
Update 3: As several HN viewers and blog commenters (Thanks James!) pointed out, the PHP, Ruby, and Node.js versions were never updated to safer alternative string comparison functions to help defend against timing attacks. While the gists have now been updated, the complete efficacy of mitigating timing attacks in these languages is debatable (i.e. https://bugs.python.org/issue15061, https://github.com/joyent/node/issues/8560). Still, the additional safety this provides is worth the slightly added complexity.
The code examples below are simplified for easier viewing and readability. In doing so, the cryptographic keys being used are hardcoded in each example. This is never safe and should not be used as-is. Hardcoded keys all too often inadvertently end up in source code repositories. Always use environment variables, untracked configuration files, or other measures to store any cryptographic keys, passwords, or the like.
Verifying HMAC-based signatures puts you at risk for timing attacks. Using constant-time string comparison functions helps to prevent this, but other factors can influence those techniques (i.e. compiler optimizations, VMs, language idiosyncrasies, etc). Use your best judgment and do your own research before implementing.
Generate an HMAC: create a JSON payload from a map and create a signature based on our shared secret.
Verify an HMAC: Given the [QUERYSTRING] from the output above, calculate our own signature and compare it with what’s provided. Then, verify the timestamp is within 30 seconds of the current time.
Generate an HMAC: Create a JSON payload from a Python dictionary and create a signature using the raw message digest. Base64 encode the signature and JSON payload.
Verify an HMAC: Given the [QUERYSTRING] from the previous output, base64 decode the signature and JSON payload, verify the signature by calculating our own digest and comparing it with the signature provided (note: Python 2.7.7 and greater can use hmac.compare_digest instead of ==). Check the timestamp against the current time and if we’re more than 30 seconds off, throw an exception.
Generate an HMAC: Create a JSON payload from a Ruby hash, sign, and base64 encode it.
Verify an HMAC: Given the [QUERYSTRING] from the previous output, calculate our own signature and compare it against the one provided. Check the timestamp against our current time and if more than 30 seconds has passed, raise an exception.
Generate an HMAC: Create a JSON payload from an associative array, sign, and base64 encode it.
Verify an HMAC: Given the [QUERYSTRING] from the previous output, calculate our own signature and compare it against the signature provided. Check the timestamp against the current time and throw an exception if the difference is greater than 30 seconds.
Verify an HMAC: Given the [QUERYSTRING] from the previous output, parse the query string into parameters, calculate our own signature, and compare it against what’s provided. Check the timestamp against the current time and if the difference is greater than 30 seconds, throw an exception.
Note: we created our own parseQuery function here because url.parse appears to automatically decode certain characters in the query string, preventing the signature comparison from working correctly.
James van Lommel
A for loop plus bitwise or may be used in languages that don’t have a constant time string comparison function. One example in PHP is here, or in several answers on stack overflow:
Thanks for the tip — the post has been updated with safer constant-time implementations.
The only example you provided that is acceptable is the Python version.
Thanks for the feedback! The post has been updated with more appropriate constant-time verification implementations.
There’s another edge-case PHP problem that could compromise your hash_equals() implementation: It’s called
mbstring.func_overload. Essentially, if an environment turned it on (e.g. for better Unicode support), this could lead to only the first (N – M) bytes (instead of N bytes) being compared and make forgeries more practical.
Thus, a solution to this would be to always use
mb_strlen($string, '8bit')instead. Ditto for any
hash_equalsis comparing hex strings I don’t think that’s an issue. Not only could multibyte strings break
ordwill choke on any non-ASCII characters as well.