Posted by & filed under Crypto, Go, NodeJS, PHP, Python, Ruby, Web development.

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.

Preamble

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.

Go

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.

Python

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.

Ruby

Generate an HMAC: Create a JSON payload from a Ruby hash, sign, and base64 encode it.

Verify an HMACGiven 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.

PHP

Generate an HMAC: Create a JSON payload from an associative array, sign, and base64 encode it.

Verify an HMACGiven 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.

NodeJS

Generate an HMACStringify a Javascript object to create a JSON payload, sign, and base64 encode it.

Verify an HMACGiven 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.

 

Sign up for Turret.IO — the only data-driven marketing platform made specifically for developers.

6 Responses to “HMAC in Go, Python, Ruby, PHP, and NodeJS”

    • tim

      Thanks for the tip — the post has been updated with safer constant-time implementations.

      Reply
    • tim

      Thanks for the feedback! The post has been updated with more appropriate constant-time verification implementations.

      Reply
  1. Scott

    Hey tim,

    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 substr() calls.

    Reply
    • tim

      Scott,

      Since hash_equals is comparing hex strings I don’t think that’s an issue. Not only could multibyte strings break strlen, but ord will choke on any non-ASCII characters as well.

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *