I've been mulling over this issue for a little while; how do you secure a web application backed by a RESTful Api? The answer varies from simple to complicated.

The Problem

First, lets consider the full problem. The important thing we need to consider about a REST Api is the it is stateless. Usually web servers retain some sort of state between itself and the client browser, which is used to maintain authentication across multiple requests.

An ideal REST interface doesn't allow this, you have to provide all authentication information with every request. It should be obvious that sending a username/password combination each time is a risk, so we need a better method.

The Simple Answer

The obvious answer is to use SSL and an authentication token.  SSL secures your connection at the protocol level, meaning you can be sure that no one is "sniffing" the data you exchange with the server.

In this scenario your application can send a username/password combination to the server once and receive an authentication token in return (which becomes your shared secret). This is then sent with each request to validate it (along with the username). SSL ensures that no one can interfere with these requests.

However there are two problems:

  • The Api is no longer truly "stateless", it is retaining (albeit in the database) your authentication token to check against each request. An ideal REST api would be able to verify a request without storing any extra temporary information.
  • If you don't, or can't, use SSL then your app is at risk from man-in-the-middle attacks.
The Complex Solution

What you need is a solution whereby the client and server have a shared secret, but this secret is never directly exchanged. Under this schema we assume that the server has been able to securely store the username and password once - the password, salted with the username, is encrypted with Bcrypt. This latter becomes our shared secret.

Then, upon each login, the client application generates the same Bcrypt hash. Great, now both client and server have the shared secret. To verify that the client is authentic, we send the MD5 of the secret and the username to the server, along with a HMAC of this data, keyed off the shared secret.

The HMAC is important; the server can pull the username/secret from its datastore, check the supplied data and re-generate the HMAC - proving that the client does have the secret key.

Now for ever request all we have to do is send the username as part of any data packet, and HMAC all of the data so that the server can verify it is genuine. All without ever sending the shared secret over the wire. The client discards username/password after it has generated the secret, for added security.

You can add all sorts of additional security (such a timestamps to mitigate relay attacks) to this, and I recommend reading this excellent tutorial which sets out many of them.

The Caveat

There are still many security issues that this doesn't resolve, and ideally any good application should use SSL (unfortunately, the use case I am looking at requires providing for clients who may well end up not using it..).