Token-based authentication - Securing the token
I have developed a backend REST API for a mobile app and I am now looking to implement token-based authentication for it to avoid having to prompt the user to login on every run of the app.
What I had in mind was on the initial request the user sends their credentials using Basic authentication over SSL. Once the server authenticates the credentials it creates a secure token and sends it back to the user so they can use it in subsequent requests until the token either expires or is revoked.
I am looking for some advice as to how I can generate a token which won't be susceptible to things like MoM/Replay attacks as well as ensuring the data stored within the token cannot be extracted out.
I am going to use the following approach to generate the token which I think would prevent any data from being extracted from it. However, I still need to make sure it's not vurnerable from other attacks.
The API will only be accessible over SSL but I am not sure if I can rely solely on this from a security perspective.
@MikeWeller OAuth is probably the universal solution to this problem, however, time constraints & complexity of development is where it falls down. Also, OAuth is probably a little bit overkill as there are no plans to open the API up to 3rd parties at the moment. The sort of token-based system I want to implement isn't far off what OAuth does, it's more like Twitters version of XAuth whereby you skip the whole login request part.
Be very careful with writing your own session management mechanism (which is essentially what you are proposing). There are dozens of common errors that inevitably occur in every new session management mechanism, which will affect yours, too. You should seriously consider how you can use an existing mechanism instead of rolling your own
The "authentication token" works by how the server remembers it.
A generic token is a random string; the server keeps in its database a mapping from emitted tokens to authenticated user names. Old tokens can be removed automatically in order to prevent the server's database from growing indefinitely. Such a token is good enough for security as long as an attacker cannot create a valid token with non-negligible probability, a "valid token" being "a token which is in the database of emitted tokens". It is sufficient that token values have length at least 16 bytes and are produced with a cryptographically strong PRNG (e.g.
java.security.SecureRandom... depending on your platform).
It is possible to offload the storage requirement on the clients themselves. In the paragraph above, what "memory" should the server have of a token ? Namely the user name, and the date of production of the token. So, create your tokens like this:
- Server has a secret key K (a sequence of, say, 128 bits, produced by a cryptographically secure PRNG).
- A token contains the user name (U), the time of issuance (T), and a keyed integrity check computed over U and T (together), keyed with K (by default, use HMAC with SHA-256 or SHA-1).
Thanks to his knowledge of K, the server can verify that a given token, sent back by the user, is one of its owns or not; but the attacker cannot forge such tokens.
The answer you link to looks somewhat like that, except that it talks about encryption instead of MAC, and that's:
- potentially insecure;
because encryption is not MAC.
+1 This is more along the lines of the type of answer I am looking for, thanks. You mention that the proposed solution for generating tokens is potentially insecure and I understand why (if it's encrypted there is the possibility for it to be decrypted). However, it's encrypted using the machine key of the server (see here) so am I right in saying it's secure as long as my server is physically secure and no-one get's a hold of the decryption key?
@James: what I mean is that the authentication token is secure as long as the attacker cannot build a fake one. Encryption does not protect against that. For instance, if using a stream cipher like RC4 (data is encryted by XORing with a key-generated pseudorandom stream), it would be trivial for a user to authenticate under his name, then fiddle with the bits to make a new valid token with another name of his choosing (as long as it has the same size as his name) (and I have seen that in a deployed banking system !). Really, if you need integrity, use a MAC.
Ah ok I see what you mean. So in terms of storage would you say it would be safe to store the token as it is generated or should that also be salted using a MAC?
"*salted using a MAC*" - think that should say *keyed* not salted. Also, is it reasonable to to store the secret in the database along with the token? Similar to how you would store a salt?
@James: the MAC is there for storage on the client (as a cookie), to prevent forgeries without having to remember client-specific state on the server. As long as you store on the server, there is no need for a MAC.
The tokens will be stored both client + server side. From the client-side point of view this will probably be in a file stored in the app settings to allow the users to be automatically logged in without having to re-authenticate. For the server side, the tokens will be stored in the database.
Based on your comments here is what I am doing (server-side). Generating a secret key of 256 bits using a .NET RNG. Then generating a HMAC using SHA12 of the username + creation date of token (using the secret key). Then finally, generating a final HMAC of username + creation date + HMAC(username + creation date) again using secret key. Does that sound about right? I can post code if need be.
For the record, the standard ASP.NET functionality linked in the original question does involve a MAC. The method name, Encrypt(), is misleading. What it actually does includes both an an HMAC (with SHA256) and encryption (with AES).
@ThomasPornin You said the token should be stored in the database. Would it not be better just to store the MAC of the token in the user sql table?
May the secret key K be a private RSA key? Or it really should be a PRNG? This key K must be unique for each token or it can be the same for all tokens generated by the server?
@Plinio.Santos I think an RSA key would be fine, I imagine it's just as secure as a PRNG key, the important points of the key is A. it can't be replicated easily & B. it's kept secret. The secret key has to be distinct per user otherwise all users would also have the same public key and there would be no distinction between user tokens.
@ThomasPornin Thank you for a very good answer. When you say that "It is possible to offload the storage requirement on the clients themselves", does it mean that the server does not need to store any information whatsoever of issued tokens? As a token is signed by the server itself and carries all relevant information (username and expiration), the server does not need to query its database to tie the token to a user and check for expiration - all data is available in the token and cannot have been altered if its signature is checked. Correct?
@JanusVarmarken: yes, that's the point. The token contents are all that the server needs to "remember", and since there is a MAC, the server can be content with not storing any per-token data, just the unique MAC verification key. Hence, no database at all! Note that such a setup implies a few restrictions: the client can omit to send back the token and thus claim to be new; similarly, the server cannot forcefully invalidate a token, since the server has no stored notion of what tokens have been issued. Usually, such client-side tokens are managed with a timeout policy.