Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HTTP Request signature without session

I am thinking of a rest web service that ensure for every request sent to him that :

  • The request was generated by the user who claim it ;
  • The request has not been modified by someone else (uri/method/content/date);
  • For GET requests, it should be possible to generate a URI with enough information in it to check the signature and set a date of expiration. That way a user can delegate temporary READ permissions to a collaborator for a limited time period on a ressource with a generated URI.

Clients are authenticated with id and a content-signature based on their password.

There should be no session at all, and so server state ! The server and the client share a secret key (a password)

After thinking about it and talking with some really nice folks, it seems there is no rest service existing to do that as simple as it should be for my use case. (HTTP Digest and OAuth can do this with server state and are very chatty)

So I Imagined one, and I'm asking your greats comments on how it should be designed (I will release it OpenSource and Hope it can help others).

The service use a custom "Content-signature" header to store credentials. An authenticated request should contains this header :

Content-signature: <METHOD>-<USERID>-<SIGNATURE>

<METHOD> is the sign method used, in our case SRAS.
<USERID> stands for the user ID mentioned earlier.
<SIGNATURE> = SHA2(SHA2(<PASSWORD>):SHA2(<REQUEST_HASH>));
<REQUEST_HASH> = <HTTP_METHOD>\n
                 <HTTP_URI>\n
                 <REQUEST_DATE>\n
                 <BODY_CONTENT>;

A request is invalidated 10 minutes after it has been created.

For example a typical HTTP REQUEST would be :

POST /ressource HTTP/1.1
Host: www.elphia.fr
Date: Sun, 06 Nov 1994 08:49:37 GMT
Content-signature: SRAS-62ABCD651FD52614BC42FD-760FA9826BC654BC42FD

{ test: "yes" }

The server will answer :

401 Unauthorized

OR

200 OK

Variables would be :

<USERID> = 62ABCD651FD52614BC42FD
<REQUEST_HASH> = POST\n
                 /ressource\n
                 Sun, 06 Nov 1994 08:49:37 GMT\n
                 { test: "yes" }\n

URI Parameters

Some parameters can be added to the URI (they overload the headers informations) :

  • _sras.content-signature=<METHOD>-<USERID>-<SIGNATURE> : PUT the credentials in the URI, not in the HTTP header. This allow a user to share a signed request ;
  • _sras.date=Sun, 06 Nov 1994 08:49:37 GMT (request date*) : The date when the request was created.
  • _sras.expires=Sun, 06 Nov 1994 08:49:37 GMT (expire date*) : Tell the server the request should not expire before the specified date

*date format : http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.18

Thanks for your comments.

like image 574
Sébastien VINCENT Avatar asked Dec 22 '10 20:12

Sébastien VINCENT


2 Answers

There are several issues that you need to consider when designing a signature protocol. Some of these issues might not apply to your particular service:

1- It is customary to add an "X-Namespace-" prefix to non-standard headers, in your case you could name your header something like: "X-SRAS-Content-Signature".

2- The Date header might not provide enough resolution for the nonce value, I would therefore advise for a timestamp having at least 1 millisecond of resolution.

3- If you do not store at least the last nonce, one could still replay a message in the 10 minutes window, which is probably unacceptable on a POST request (could create multiple instances with same values in your REST web service). This should not be a problem for GET PUT or DELETE verbs.

However, on a PUT, this could be used for a denial of service attack by forcing to update many times the same object within the proposed 10 minutes window. On a GET or DELETE a similar problem exists.

You therefore probably need to store at least the last used nonce associated with each user id and share this state between all your authentication servers in real-time.

4- This method also requires that the client and servers be clock synchronized with less than 10 minutes skew. This can be tricky to debug, or impossible to enforce if you have AJAX clients for which you do not control the clock. This also requires to set all timestamps in UTC.

An alternative is to drop the 10 minutes window requirement but verify that timestamps increase monotonically, which again requires to store the last nonce. This is still a problem if the client's clock is updated to a date prior to the last used nonce. Access would be denied until the client's clock pass the last nonce or the server nonce state is reset.

A monotonically increasing counter is not an option for clients that cannot store a state, unless the client could request the last used nonce to the server. This would be done once at the beginning of each session and then the counter would be incremented at each request.

5- You also need to pay attention to retransmissions due to networks errors. You cannot assume that the server has not received the last message for which a TCP Ack has not been received by the client before the TCP connection dropped. Therefore the nonce needs to be incremented between each retransmission above the TCP level and the signature re-calculated with the new nonce. Yet a message number needs to be added to prevent double execution on the server: a double POST would result in 2 object being created.

6- You also need to sign the userid, otherwise, an attacker might be able to replay the same message for all users which nonces have not yet reached that of the replayed message.

7- Your method does not guaranty the client that the server is authentic and has not been DNS-hijacked. Server authentication is usually considered important for secure communications. This service could be provided by signing responses from the server, using the same nonce as that of the request.

like image 161
Jean Vincent Avatar answered Nov 20 '22 13:11

Jean Vincent


I would note that you can accomplish this with OAuth, most notably "2-legged OAuth" where client and server share a secret. See https://www.rfc-editor.org/rfc/rfc5849#page-14. In your case, you want to omit the oauth_token parameter and probably use the HMAC-SHA1 signature method. There's nothing particularly chatty about this; you don't need to go through the OAuth token acquisition flows to do things this way. This has the advantage of being able to use any of several existing open source OAuth libraries.

As far as server-side state, you do need to keep track of what secrets go with which clients, as well as which nonces have been used recently (to prevent replay attacks). You can skip the nonce checking / lifetimes if you run things over HTTPS, but if you're going to do that, then HTTPS + Basic Auth gives you everything you described without having to write new software.

like image 28
Jon Moore Avatar answered Nov 20 '22 12:11

Jon Moore