Secure Cookie Authentication for CouchDB

I’ve recently been working on an implementation of cookie-based authentication for CouchDB. This is important for pure CouchDB applications (couchapps), where browsers communicate directly with CouchDB. Currently browsers can be authenticated using HTTP basic auth but the popup login box can be disruptive and confusing for users.

EPIC BEARD

Implementation

The cookie part itself was pretty straightforward. The basic idea is that once the user has been authenticated via a traditional form-based login, they are given a timestamped, tamper-proof token in the form of a cookie. Whenever CouchDB receives a cookie with a valid token, it authenticates the user for that request. It then sends a new token with a new timestamp. For performance reasons, it only sends a new token if the timestamp is more than something like 10 minutes old.

No state is stored on the server-side, so that this can be easily used in a cluster. Note that the clocks need to be synchronised somewhat so that the timestamps can be verified.

The cookie is of the form:

username + ':' + timestamp + ':' + HMAC(username + ':' + timestamp)

where HMAC is a secure HMAC signature e.g. HMAC-SHA-256. The timestamp and signature together provide a level of forward-security i.e. a passive eavesdropper cannot re-use a captured cookie in the future outside of the valid timestamp window. This protects against cookies being re-used in the event that a hard disk is stolen and the cookies haven't been erased.

The code is here: http://github.com/jasondavies/couchdb/tree/cookie-auth

Usage

Add the following to local.ini:

[httpd]
authentication_handler = {couch_httpd_auth,cookie_authentication_handler}

I have added per-db _login and _logout handlers to default.ini, but if you want to set up per-node login/logout handlers, use something like the following:

[httpd_global_handlers]
_login = {couch_httpd_auth, handle_login_req, "userdb"}
_logout = {couch_httpd_auth, handle_logout_req, "userdb"}

The final step is to create a special _design/_auth design document containing a users view mapping usernames to {password_sha: base64(sha1(password)), salt: <random salt>, roles: [<role name>, ...]} and a secret member.

Logging in and out should now be as simple as POSTing a username and password to http://127.0.0.1:5984/mydb/_login. An optional query parameter next may also be added to specify a redirect for successful login/logout.

Authentication Protocols

There is a plethora of authentication protocols out there, of varying degrees of security. The vast majority of Web sites use a simple plain-text form-based login though. Why? Firstly for most Web sites, the security requirements are pretty low. Who's going to want to steal my Digg login? For higher security just add TLS/SSL and immediately you are protected against a variety of active and passive attacks. The other reason is that if JavaScript is turned off, the only alternative to form-based plaintext login is HTTP Basic or Digest, where the designer has no control over the login UI. HTTP Basic is essentially equivalent to plain-text, and digest is slightly more secure but still has a bunch of problems.

With CouchDB's strong emphasis on JSON and JavaScript, I wanted to find out if I could crank up security a notch for unsecured HTTP if I assumed that JavaScript is enabled on the client.

Ideal Requirements

  1. Reasonable performance/simplicity of JavaScript implementation
  2. Mutual authentication
  3. Resistance to off-line dictionary attacks based on passive eavesdropping
  4. Passwords stored in a form that is not plaintext-equivalent
  5. Limited resistance to replay attacks i.e. outside a certain time window

SRP

Invented by Tom Wu in 1998, the Secure Remote Password protocol is highly impressive. It has the following properties:

  • Resistant to dictionary attacks by both passive and active network intruders
  • Offers perfect forward secrecy, which protects past sessions and passwords against future compromises
  • User passwords are stored in a form that is not plaintext-equivalent to the passwords themselves

Wow!

Cryptography in JavaScript

Drunk on the awesomeness of SRP, I went away and implemented it. Unfortunately, I discovered that the performance of modular exponentiation in JavaScript is not good enough for SRP to be usable with a sufficiently large modulus (1024 bits) on today's machines. A 256 bit modulus performs perfectly well but this is not really big enough to resist attacks by an organisation with sufficient resources.

From Tom Wu:

Unfortunately, 256 bits is way to small for any decent amount of security. 256 bits is less than 80 digits, and numbers of that size can be factored on a desktop PC in a few minutes these days. Even 512 bits is starting to look insecure for “casual” use.

Active Attacks

There is another problem here, which is that the JavaScript client itself will be transmitted over the same insecure network over which the authentication protocol is communicated. SRP itself assumes that the client is trusted, but this particular scenario is vulnerable to an active attacker who might simply inject a fake JavaScript client that, for example, sends the user's password in plaintext over the network for the attacker to capture.

The only way to prevent this would be to have an unspoofable browser UI of some kind (e.g. provided by a browser addon). Signing the JavaScript code would be another possibility but this is currently only supported by Mozilla browsers.

SCRAM and other alternatives

Tom Wu suggested looking at CHAP or SCRAM as a potential auth protocol that would still be benefitial to use over HTTPS.

Due to limited time, for now I've opted for a simple plain-text form-based login in my branch, which as I mentioned above is what everyone else uses anyway. It's good enough for most purposes and for everything else we have TLS/SSL. Something like SCRAM would be preferable though, as the password itself is never actually sent to the server, even if TLS/SSL is used.

Conclusion

SRP is impressive, the only other authentication protocol with similar properties that I know of is J-PAKE. However, a JavaScript implementation is unsuitable on today's browsers due to the slow performance of modular exponentiation at 1024 bits. In addition, serving the client implementation over an unsecured connection is itself vulnerable to active attacks, thus for CouchDB, a simpler protocol like SCRAM will give us most of the properties we need over an unsecured HTTP connection, and one can simply add TLS/SSL to protect against active attacks.

Photo credit: Eliot Phillips