Using API keys effectively

May 12, 2011

A common use of API keys for authentication of web api consumption is to ask the requester to just include the key directly in the URI parameters of the web API call as illustrated below:

The term ‘key’ in this case can be misleading. A key is normally used to perform some sort of crypto operation, typically a signature. The use of the API key above is the same as using a password in clear such as in:

In both cases, nothing is signed, and the shared secret is sent alongside each call. If the request is somehow sniffed by a malicious intermediate (think MITM), the malicious user can now impersonate the legitimate requester. A secure channel to send such messages is needed. Even on a secure channel, this type of approach causes a number of security issues. For example, you want to avoid these shared secrets showing up in your traffic logs or being rendered to web pages for a browser based portal.

Other well known API service providers (such as AWS, Azure) use an HMAC signature based authentication model. HMAC (Hash-based Message Authentication Code) uses a hash function combined with a symmetric key. It still uses a shared secret but in this case, the secret is not included in the requests. Instead, the request includes an HMAC signature added to the Authorization HTTP header (the RESTful location for such signatures, tokens). This HMAC covers essential parameters such as the HTTP VERB, the payload, the payload type, a date, etc. Even if the request can be intercepted, the HMAC cannot be re-used beyond a short period of time and cannot be used if any of these critical aspects of the request are altered in any way. Using the same shared secret, the recipient can verify the authenticity of the message and the identity of the requester. Authentication and integrity are both achieved.

Below, an example HMAC construct as used by AWS:

Authorization: AWS + KeyId + : + base64(hmac-sha1(VERB + CONTENT-MD5 + CONTENT-TYPE + DATE + …))

Using the Layer 7 API Proxy, you can use such HMAC signatures to authenticate incoming requests on behalf of a protected API and to add signatures on the way out using the Generate Security Hash Assertion as illustrated below.

Layer 7 Gateway Hashing Assertion

Layer 7 Gateway Hashing Assertion

The Generate Security Hash Assertion lets you calculate an HMAC based on the key and data to sign. The data to sign is something that must be agreed upon in advance, as is the way to incorporate the HMAC in the request. When working with an existing system which already defines this (such as AWS), you simply set the variable ${hash.dataToSign} to reflect the same order and contents. If you have the freedom to define this yourself for your own environment, make sure it covers key aspects of a request so that an HMAC cannot be reused if it falls in the wrong hands. For a RESTful web service for example, it makes sense to cover the HTTP verb (method), the request URI, query parameters and payload if any. Adding either a timestamp or a validity period is also good practice.

Once you calculated an HMAC in your policy using this assertion, you can inject it to an outgoing message by adding it to the Authorization HTTP header directly as illustrated below. Note that you can include this HMAC in any desired header.

Injecting an HMAC downstream

For verifying an incoming HMAC, construct your policy to calculate the hash based on the input and compare this value against the incoming HMAC value using a simple comparison assertion.

Validating an incoming HMAC

Validating an incoming HMAC