Recently I had to explain how would I design a REST Authentication. My humble thoughts:

Using Basic or Digest is naive.

1. Basic token is a plain Base64 text. To make it worst, you need to send the username and password each time as part of the HTTP packet so any MitM attacker will be able to translate it.

2. The security aspect could be resolved by using Basic over SSL. Yet SSL has it’s performance drawbacks. And in your requirements SSL may not be an option.

3. Using the Digest as being secure, resolves 1 and 2, yet it has disadvantages as:
- The nonce is optional. Skipping the nonce MD5(username:nonce:password) could end up with Digest algorithm of MD5(username:password) which is not secure for simple passwords. Any computer today would be able to reverse engineer a simple password MD5 hash using available dictionaries.
- The Digest is optional. A MitM could tamper the packet and browser could fall back to Basic.

4. Digest and Basic when invoked within a browser environment has the annoying feature of opening a native popup with username/password.

5. Basic/Digest don’t expire. As long as you stole a Basic/Digest security token you can continue with the replay attack.

6. No prevention for tampering. Digest algorithm includes hashing the URI:Method:andBody yet, those algorithms are once again recommendations only and optional.

My prefer approach would be a

Custom Authentication Token a la Amazon

Amazon REST Authentication did a great job of providing a secure REST Authentication. Let’s dissect their token:

Example AWS Call:

GET /photos/puppy.jpg HTTP/1.1
Host: johnsmith.s3.amazonaws.com
Date: Mon, 26 Mar 2007 19:37:58 +0000
Authorization: AWS 0PN5J17HBGZHT7JJ3X82:frJIUN8DYpKDtOLCwo//yllqDzg=

where Authorization algorithm header is:

Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature;

and signature:

Signature = Base64( HMAC-SHA1( UTF-8-Encoding-Of( YourSecretAccessKeyID, StringToSign ) ) );

and more:

StringToSign = HTTP-Verb + "\n" +
	Content-MD5 + "\n" +
	Content-Type + "\n" +
	Date + "\n" +
	CanonicalizedAmzHeaders +
	CanonicalizedResource;

CanonicalizedResource = [ "/" + Bucket ] +
	<HTTP-Request-URI, from the protocol name up to the query string> +
	[ sub-resource, if present. For example "?acl", "?location", "?logging", or "?torrent"];

CanonicalizedAmzHeaders = <described below>

Explaining:

  • CanonicalizedAmzHeaders: Some special AWS business headers. Skipping..
  • CanonicalizedResource: a string concatenating elements from HTTP packet being sent – URI, parameters, Method etc. Reason? Preventing HTTP packet tampering. Assuming an attacker stole the Authentication token, then stolen token could be used only within the current REST call as vital elements of the packet are being injected as part of AWS token.
  • StringToSign and Content-MD5 etc.. – more tampering prevention and obfuscation.
  • Signature: One way hash encryption of the whole amalgam above.

More  thoughts:

  • The token will contain a time stamp so it will expiry on inactivity.
  • The token could optionally contain business sensitive data too. userID or any businessID’s.

Lastly, depending on requirements, you may want to consider using OAuth too.

Have a crafty and secure fun!