To start with a little background, then I will outline the options for authentication of HTTP based server APIs with a focus on HMAC and lastly I will provide some tips for developers building and using HMAC based authentication.
Recently I have been doing quite a bit of research and hacking in and around server APIs. Authentication for these type APIs really depends on the type of service, and falls into a couple of general categories:
For infrastructure APIs I have had a look at a few options, these are explained in some detail below.
This is the simplest to implement and for some implementations can work well, however it requires transport level encryption as the user name and password are presented with ever request. For more information on this see Wikipedia Article.
This is actually quite a bit closer to HMAC than basic, it uses md5 to hash the authentication attributes in a way which makes it much more difficult to intercept and compromise the username and password attributes. Note I recommend reading over the Wikipedia page on the subject, in short it is more than secure than basic auth, however it is entirely dependent on how many of the safeguards are implemented in the client software and the complexity of the password is a factor.
Note unlike basic authentication, this does not require an SSL connection, that said make sure you read the Wikipedia article as there are some issues with man in the middle attacks.
Hash-based message authentication code (HMAC) is a mechanism for calculating a message authentication code involving a hash function in combination with a secret key. This can be used to verify the integrity and authenticity of a a message.
Unlike the previous authentication methods there isn’t, as far as I can tell a standard way to do this within HTTP, that said as this is the main authentication method used by Amazon Web Services it is very well understood, and there are a number of libraries which implement it. To use this form of authentication you utilise a key identifier and a secret key, with both of these typically generated in an admin interface (more details below).
It is very important to note that one of the BIG difference with this type of authentication is it signs the entire request, if the content-md5 is included, this basically guarantees the authenticity of the action. If a party in the middle fiddles with the API call either for malicious reasons, or bug in a intermediary proxy that drops some important headers,the signature will not match.
The use HMAC authentication a digest is computed using a composite of the URI, request timestamp and some other headers (dependeing on the implementation) using the supplied secret key. The key identifier along with the digest, which is encoded using Base64 is combined and added to the authorisation header.
The following example is from Amazon S3 documentation.
"Authorization: AWS " + AWSAccessKeyId + ":" + base64(hmac-sha1(VERB + "\n" + CONTENT-MD5 + "\n" + CONTENT-TYPE + "\n" + DATE + "\n" + CanonicalizedAmzHeaders + "\n" + CanonicalizedResource))
Which results in a HTTP request, with headers which looks like this.
PUT /quotes/nelson HTTP/1.0 Authorization: AWS 44CF9590006BF252F707:jZNOcbfWmD/A/f3hSvVzXZjM2HU= Content-Md5: c8fdb181845a4ca6b8fec737b3581d76 Content-Type: text/html Date: Thu, 17 Nov 2005 18:49:58 GMT X-Amz-Meta-Author: firstname.lastname@example.org X-Amz-Magic: abracadabra
Note the AWS after the colon is sometimes known as the service label, most services I have seen follow the convention of changing this to an abbreviation of their name or just HMAC.
If we examine the Amazon implementation closely a few advantages become obvious, over normal user names and passwords:
As far as disadvantages, there are indeed some:
As I am currently developing, and indeed rewriting some of my existing implementations I thought I would put together a list of tips for library authors.
From a security stand point a couple of basic recommendations.
So in closing I certainly recommend using HMAC authentication, but be prepared to learn a lot about how HTTP works and a little Cryptography, this in my view cant hurt either way if you’re building server side APIs.
Based on some of the comments made when I submitted this to hacker news I have compiled some extra links and observations.
One interesting point made was the issue of replay attacks, which is where a valid message is maliciously or fraudulently repeated or delayed. This is either performed by the originator or by a man in the middle who retransmits the message, possibly as a part of a denial of service.
To protect from these types of attacks a Cryptographic nonce, which is an arbitrary number usable only once in a message exchange between a client and a server. These are in fact optionally used within Digest authentication mentioned previously.
One of the comments linked an article which suggested use of nonce with HMAC as described in RFC 5849 The OAuth 1.0 Protocol. In this specification an nonce is paired a timestamp and included with each message, the timestamp can be used to avoid the need to retain an infinite number of nonce values for future checks, the server can reject messages with timestamps older than window of time nonce values are retained.
Based on the original post I have developed a flexible hmac authentication library called Ofuda for nodejs, this currently contains a small routine to hmac sign a list of headers for a given request. In the near future I plan to add validation of a signature and an implementation of nonce based on the aforementioned strategy.blog comments powered by Disqus