5

I'm implementing a set of RESTful services for some developments and one of these is an authentication service.

This authentication service authenticates two kinds of identities:

  • Applications. AppKey-based authentication so clients must register for a key in order to access to the rest of the services.
  • Users. Well-known credentials (user+password)-based user authentication so humans and machines can work with these RESTful services through client applications.

These RESTful services are stateless.

When a client application authenticates against the authentication service, or when a human or machine authenticates as an identity using credentials, both operations generates an AppToken and UserToken respectively.

These tokens are a salted hash so subsequent requests to the RESTful infrastructure will be authenticated without sharing AppKeys and credentials.

Form the point of view of a fully stateless approach, these tokens should be stored no where in the service layer but in some kind of client-side state (f.e., a Web client would store it using HTTP cookies). This is how my current implementations are working right now.

Because re-authenticating each request using these tokens and let the service layer receive the token coming from the client so it can compare what token comes from the client and check if it's a valid token re-generating it in the service layer and compare with the one owned by the client is too expensive, I've implemented a service layer AppToken and UserToken, both having an expiration date and an owner (the application or user for which the token have been created for), in order to check if the token coming from the client exists in the token store.

How does clients interactively unauthenticate? Just dropping client-side security state. If it's a Web client, it drops the authentication cookie and just refreshing the page, client detects no authentication cookie and user is redirected to the login page.

From the point of view of RESTful services, this is a stateless unauthentication: clients aren't aware about the trick of having a service layer pseudo-authentication state. It's just a service implementation detail - a performance optimization -.

I'm not going to list the pros of stateless services because I'm absolutely sure that this approach is the way to go, but I find a problem: stateless authentication/unauthentication means that clients don't notify server that they close their session, so the security store ends with a lot of useless records.

This isn't a great problem if service clients are ones that would have limited time sessions (f.e., 1 hour, 3 hours, a day...), but what happens if an user must be authenticated forever (8 months, a year)?. How do you distinguish what's an an expired token?

There're some approaches in order to solve this situation:

  1. Whenever the service layer receives a request, it updates token expiration date, so an automated process may drop those tokens that have expired defining an arbitrary expiration of tokens (f.e. 24 hours).

  2. Compromise stateless nature of the architecture and let clients notify service layer that they don't want to be authenticated anymore, so service can drop the associated token to the client session (But wait... what happens if client closes a Web client? User will never actively notify service that the token must be dropped... So... Zombie tokens are there yet, so an automated process should drop them, but... what's a zombie token? I don't like this approach).

  3. Completely stateless authentication, no store, per-request authentication.

This is the question! What's your suggested approach - even if it's not 1., 2. or 3. - and why?

Thanks for this long reading - I honestly believe that question's conclusions are going to be extremely useful to anyone -!

Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
  • What checks are you doing in the service layer on the token and how come they are so expensive? – Henrik Aug 29 '12 at 09:52
  • @Henrik The token is composed by some user info + secret salt + date emitted – Matías Fidemraizer Aug 29 '12 at 10:02
  • Who is the salt secret for? Does each service share knowledge of the salt, or only the authentication service? – Henrik Aug 30 '12 at 06:42
  • @Henrik It's implemented at the business level. This secret is handled by authentication manager. By the way, maybe I'm lost, but it seems that this detail has nothing to do with my question. – Matías Fidemraizer Aug 30 '12 at 07:00
  • Salts are for increasing entropy so that you can't use rainbow tables on a whole range of digests/hashes, but only on a single hash + salt at a time. I'm not sure this is what you're after. So if your services don't know the salt, they don't know how to recreate the hash, and so then you need each service to talk to the authentication manager each time, because that's where the knowledge is. – Henrik Aug 30 '12 at 07:06
  • @Henrik Sorry, AppToken/UserToken isn't using a secret service owned salt. This secret salt is for hashing passwords. Sorry for this wrong info! – Matías Fidemraizer Aug 30 '12 at 14:07

2 Answers2

7

Number three

Stateless authentication, token-based. Assuming transport-level encryption.

  • [X]SS -- X signed by S's public key
  • [X|Y] -- X and Y in same envelope
  • Y [M]SY -> S -- Y sends signed message M to S.

Aim: Client C wishes to speak to service S.

  1. Client C sends its shared secret or public key PKC for authentication to service A, for which C knows the endpoint and public key (PKA).

  2. A [now + interval | user-id or PKC]SA -> C

    Explained:

    Service A adds an interval to the current date/time as an expiration. In the buffer-to-be-sent is now the expiry date and the user id, PKC (assuming you have a valid Identity Provider).

    [now + interval | user-id or PKC] = T

    A signs it;

    [T]SA

  3. Client C wishes to talk to backend service S.

  4. C [[M|[T]SA]SC -> S

    C sends message M plus the token it got signed from A, to service S.

  5. S cares that C really did send it and verifies C's signature SC that it reads from the envelope.

    S verifies signature SA of token. Failure means request is denied.

    S verifies token [T]SA: user-id/PKC correct and token date >= now. Expired token means to send 'token expired' message to client C. Permission denied if token has faulty signature.

    (Optional; S authorizes C, digression)

  6. S performs work and sends back [M2]SS to client C.

This wouldn't be too much overhead; verifying the signature is a pretty fast operation.

Certificates

The question 'C# Sign Data with RSA using Bouncy Castle' shows how you sign and verify a piece of string, the message that you are sending.

You'll need the certificates; if you are using a configuration manager (YOU SHOULD BE DOING THAT! ;)), like puppet, then you create a certificate signing request (CSR) and then sign it using puppet.

Post Scriptum on unauthentication in particular

There's something called a certificate revocation request, which basically is a laundry list of public keys that have been withdrawn and are not to be trusted/used. Place PKC there and broadcast the revocation, and it basically acts by requiring the client to do another certificate signing request round.

Also, if you want the ability to expire specific tokens, add a unique ID (UUID/GUID) to the token T when you create it, and have a token revocation list, similarly broadcasted when changed, that you purge the token UUIDs from when they expire. So a service would then also check the token revocation list if the received T is in it.

Hash-based tokens

Have a look at the software giants are doing. E.g. Amazon's REST interface, that uses shared secret keys:

The Amazon S3 REST API uses a custom HTTP scheme based on a keyed-HMAC (Hash Message Authentication Code) for authentication. To authenticate a request, you first concatenate selected elements of the request to form a string. You then use your AWS Secret Access Key to calculate the HMAC of that string. Informally, we call this process "signing the request," and we call the output of the HMAC algorithm the "signature" because it simulates the security properties of a real signature. Finally, you add this signature as a parameter of the request, using the syntax described in this section.

Read more on Amazon's scheme.

Subversion/Attack Vectors on the Above

  • The scheme I described initially requires SSL, which is vulnerable to the numerous certificate authorities out there, as well as a number of other things.
  • You are left vulnerable to replay attacks, i.e. a man in the middle re-sending a message. If your REST interface is idempotent you are safe. You are also safe if you add a server-known cryptographic nonce to the requests.
Community
  • 1
  • 1
Henrik
  • 9,714
  • 5
  • 53
  • 87
  • Thanks for your answer. Let's see if others have other opinions. Completely stateless authentication is a good option. – Matías Fidemraizer Aug 30 '12 at 14:05
  • I've a question about your answer. How service verifies that a token is valid? I mean that because ok, authentication service emits a *token* and a Web client stores it as a token. Next requests authenticates the application or user using this token. Since a token is a hash you cannot revert it to know what's its source plain text. How you authenticate this token in the service? Do you consider any token received from some application with a valid "AppKey" (client secret) eligible for authenticating the request? – Matías Fidemraizer Aug 30 '12 at 14:28
  • (continuation) ... Since connection uses HTTPS/SSL, and application does requests to the service using an unique secret (the AppKey), the pair of secret (appkey) + token should be secure enough to understand that some request is authenticated. In the other hand, in a Web client token is stored in a cookie and token expiration is equal to the cookie expiration. If cookie expires, token too. As you said in your answer, cookie expiration would be increased after each successfully authenticated request to the service layer. Am I wrong? – Matías Fidemraizer Aug 30 '12 at 14:33
  • We're talking about two different things. What I am suggesting in my response above is that you introduce public key infrastructure, because it can solve three problems for you; authentication, verification of message contents and encryption. The public key of C in the above example is the only identifier that the authentication service needs in order to find the identity of the caller. The token is signed by the authenticator, thereby making it intractable for any other agent to falsify the validity of when the token expires or who it is for. ... – Henrik Aug 30 '12 at 16:09
  • If you were doing it with hashes, then you need to have a shared secret amongst your services, but in order to reverse the hash, to see what it means, you will need a lookup of some sort; this is what you were talking about being slow (an extra call for every request that S receives, to A or a lookup dictionary; `token -> (expiry, uid)`; so I explained a different way, using signatures. – Henrik Aug 30 '12 at 16:11
  • Finally, you ask about cookies; the above protocol doesn't care about how you transfer or expire the token, because *when* it is expired, no client would use it: it would gain a client nothing. Instead, when the token expires, the client would request a new token. – Henrik Aug 30 '12 at 16:12
  • I see. When you say "sign token" you mean using an asymmetric/symmetric encryption algorithm so service layer can decrypt the token to plain text. Expiration date and so on would come in the token itself. Am I wrong? – Matías Fidemraizer Aug 30 '12 at 16:44
  • Yes, unfortunately, you are. I'm talking specifically about signing, not encryption. Signing "means that" you generate a hash(the content, your private key), so that the verify(content, your public key) = true (fast to verify). The actual signed contents is plaintext, which is why I state the assumption that we're doing this over SSL/HTTPS, at the top of my answer. However, with the PKI in place, it's pretty trivial to also encrypt the contents with those keys, but that's for another question ;) – Henrik Aug 30 '12 at 16:50
  • Ok, thanks for your effort. Certainly your info will contribute to choose the right solution! – Matías Fidemraizer Aug 30 '12 at 21:56
  • After analyzing your answer, I believe you had some misunderstood of my question. AppKey/AppToken is a paradigm for identifying and you thought I was talking about encryption. I'm looking to simply unauthenticate an identity (in our case, this identity is an application and an user). Maybe I'm absolutely crazy and you are rightly answering my question, but I believe that you're describing a public key encryption system rather than authenticating/unauthenticating in the cloud. – Matías Fidemraizer Sep 03 '12 at 11:09
  • You mean like this: http://developers.kewego.com/headlines/advanced-practices/generate-apptoken.html ? – Henrik Sep 03 '12 at 15:18
  • In my example, the AppKey = `PK_C` and AppToken = `[T]S_A` – Henrik Sep 03 '12 at 15:19
  • You'll need crypto no matter what; what I'm talking about isn't *encryption* though. I never encrypt anything in my answer, but rely on the underlying SSL PKI to do that. – Henrik Sep 03 '12 at 15:21
  • Double-checking again your answer, I understand that when you show how to sign a token you're worried about "who delivered the token" (you want a way of be 100% sure that your server and no other has generated that token). That's ok, you're right: a completely secure token would be the one that can be verified by the server and validated as a one delivered by itself. Anyway, just skip that part. I've a question about your answer: if token expiration is sent as part of the HTTP request through SSL (so you encrypted at the transport level, I'm agree with you)... – Matías Fidemraizer Sep 04 '12 at 07:56
  • at the end of the day, when it's stored as a secure cookie in the client-side, what prevents the users from opening a Web debugger like Firebug, Opera Dragonfly or whatever and change that expiration even if the cookie is HTTP-ONLY? There's some weakness in your argument or I'm missing something. That's why I think that your approach needs a token which is itself an encryption of the security token and expiration date (for example: "ejjepejop3u903903j3j93309j39903jdpdpod;2012-02-02 8:00"). What do you think? – Matías Fidemraizer Sep 04 '12 at 08:01
  • The reason the client can't bump the expiration time of the token, is that the expiration time is *within* the *signed* token, so if the client manipulates the value, the signature of the token is not correct any more, and the token is rejected at the server. – Henrik Sep 04 '12 at 08:08
  • Ok, now I've understood the approach and its details. It's what I'm looking for (actually I was looking for fully stateless authentication and unauthentication). Thanks for your info. Some suggestion: I would rephrase your answer in a more common language because you use cryptography semantics which isn't near to most developers. Thanks! – Matías Fidemraizer Sep 04 '12 at 09:29
-1

Chosen approach: COMPLETELY STATELESS AUTHENTICATION AND UNAUTHENTICATION

Finally, I got a conclusion and a protocol in order to switch to the whole completely stateless token-based authentication and unauthentication.

How to achieve it?

First of all, this is what you need to have stateless token-based authentication for applications (but user authentication would work in the same way, excluding this inventory):

  • An application registration system. An application is an access to your services. It's "your application accessing some services on the net (intranet, internet, cloud...). This is creating application keys (skip this for user authentication).
  • A server certificate so client to service connections are encrypted by using HTTPS/SSL.

This is the flow of authenticating an application:

  1. Client sends an authentication request to the authentication service. This request must include the Application Key (AppKey).

  2. Authentication service receives the previously sent request.

  3. Now authentication service creates an application token (AppToken), which is a self-describing concatenation of the necessary information to track a concrete authenticated client to the services relying on authentication service.

  4. AppToken is a compound string (this composition can be an object serialized using JSON) of:

    • An application hash (*a SHA - or other - which is the result of concatenate some application info. This is info will be a service secret + Expiration date (which is part of the token itself). Why Expiration date?. Imagine that a man in the middle or something can break security and modify token's expiration? When encrypted token gets decrypted in order to authenticate a request, the result of hashing again the expiration date + AppKey will no longer produce the same hash, so token gets invalidated.
    • Issued date. Current UTC Date+Time when creating the token.
    • Expiration date. An UTC DateT+Time on which the token will be no longer valid.
  5. Authentication service encrypts step #4 result (the JSON-serialized object). **Use AppKey as the key or password for a symmetric cipher. In my case, I'll use Rijndael for that.

  6. Subsequent request will include this token in order to avoid sending plain text credentials. Those request will always include the AppKey too, so authentication service will be able of identify what application is trying to authenticate the request.

  7. After some time, a token becomes expired or invalid, and client requests for a new AppToken. Or the client was closed by the user and there's no persistent storage that would save security tokens, so next client session will request new ones when needed.


Some hints and details about .NET implementation of such authentication method:

  • I've used System.Security.Cryptography.RijndaelManaged class for symmetric encryption. Both AppKey and AppToken (and in case of token-based user authentication it's almost the same solution) are generated using RijndaelManaged class.

  • Encrypted text is converted to an HEX string. This is sent with the authentication response. In our case (a RESTFul API), the HEX string representing the AppToken will be sent as a response header. Whenever a request includes this HEX string, authentication process will reconvert it to the original encrypted text, and later it'll get decrypted in order to evaluate if the token is valid.


Thanks Henrik for your effort. I've taken some of concepts in your own answer and I've mixed them with my own conclusions.

Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
  • This isn't what I wrote though ;). I will write some code instead and include that, because that's unambiguous. – Henrik Sep 04 '12 at 19:02
  • @Henrik It's a summary of my conclusions based on your answer and my own investigation. I believe that your answer is a good one, but it's too techie, as I said in some comment. Don't think that anyone will understand it. I think Stackoverflow should provide direct, easy to understand answers. That's why I tried to summarize everything in a single text :D – Matías Fidemraizer Sep 04 '12 at 21:05
  • Yes, that's good, but you've changed some pieces of it, which is why I'd rather improve my own answer... :) – Henrik Sep 04 '12 at 21:13
  • @Henrik And, why not? It's not about creating a competition but contributing to the community and share our knowledge !! – Matías Fidemraizer Sep 04 '12 at 21:20
  • I mean, you've changed some important pieces that breaks the protocol (or changes protocol). But I don't think we'll get further in comments right now. – Henrik Sep 04 '12 at 21:23
  • @Henrik Again, it's a summary. It's not the absolute solution. But it gives the right hint to to it right. I'm not wrong if I think that your answer (thanks for the effort, eh!), is too complex and it doesn't answer the question at all. I asked for "I want to do X, suggest me an approach" and you opted-in to make a cryptographic-style "formula". Stackoverflow isn't a maths forum, you know that, so, I honestly think that you should rephrase your answer so other can take advantage of what's behind your too complex text. Since it seems you won't do it, I did it for you in a short summary. – Matías Fidemraizer Sep 05 '12 at 06:04
  • Yes, and it's great that you did so, I have no objection to that! (albeit you didn't ask me to rephrase till yesterday). – Henrik Sep 05 '12 at 12:18
  • Although, it's not your text that is the answer, so I do object to removing the answer from my post after all the help I have given you. – Henrik Sep 05 '12 at 13:08
  • @Henrik I've finally changed my mind of marking my own one as the right answer because I believe most of developers expect something like mine rather than a too complex to understand text - if you don't have deep cryptography knowledge -. Anyway, check my mention to your effort in my answer. And since 1st day you wrote your answer, I gave you a +1, which is a good demostration of my gratitude about your cooperation in finding a good solution :) – Matías Fidemraizer Sep 05 '12 at 17:34