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.
In short, you should use a cryptographic strength one-time random token, and hash it in the database.
- must be allowed to be used only once,
- must only be usable for the user it was created for,
- must only be sent via HTTPS,
- should have an expiry date (e.g. 7 days).
Once the user logs in with the token, it is invalid and a new token should be created and given to the user. In the case of an expired token, the user must be made to log in again, using their real credentials.
A more definitive and lengthy description of these rules can be found in part 2 of The Definitive Guide To Forms based Website Authentication:
Persistent Login Cookies ("remember me" functionality) are a danger zone; on the one hand, they are entirely as safe as conventional logins when users understand how to handle them; and on the other hand, they are an enormous security risk in the hands of most users, who use them on public computers, forget to log out, don't know what cookies are or how to delete them, etc.
Follow Charles Miller's 'Best Practices' article. Do not get tempted to follow the 'Improved' Best Practices linked at the end of his article. Sadly, the 'improvements' to the scheme are easily thwarted (all an attacker has to do when stealing the 'improved' cookie is remember to delete the old one. This will require the legitimate user to re-login, creating a new series identifier and leaving the stolen one valid).
And DO NOT STORE THE PERSISTENT LOGIN COOKIE (TOKEN) IN YOUR DATABASE, ONLY A HASH OF IT! The login token is Password Equivalent, so if an attacker got his hands on your database, he could use the tokens to log in to any account, just as if they were cleartext login-password combinations. Therefore, use strong salted hashing (bcrypt / phpass) when storing persistent login tokens.
Update: Sorry, I misunderstood the question. The method you linked looks reasonable, but it won't protect you from replay attacks or man-in-the-middle. You should use SSL alongside it.
"*cryptographic strength one-time random token*" would you say the approach I linked to is secure enough for this? "*Once the user logs in with the token, it is invalid and a new token should be created*" Not quite sure if I understand how that would work. Are you saying tokens should be invalidated/re-issued on a per request basis?
@James When the user comes to the site and does not have a session, the cookie token is sent to the server. The server validates it, creates a new session as the logged in user, and invalidates the token. It then gives the user a new token. The session identifies them whilst they're using the site, but when they close their browser (or the session expires) the user is no longer logged in. When they come back, they have no session, and the new token you gave them allows you to create a new session (go back to step 1)
@Polynomail There is no notion of a cookie, session or browser in my case. This is a REST API which is accessed via a mobile app.
@James Sorry, misunderstood the question. The mechanism you linked is fine for this kind of situation, but it will not protect you against replay attacks. You need to use something like SSL to prevent MitM / replay.
The API is running over HTTPS so is it safe to assume I wouldn't be vurnerable to those sort of attacks? I guess I am probably being overly paranoid, however, when it comes to security on the web I think it's a good thing to be like that!
Yup, you should be fine if it's over HTTPS and your client refuses invalid certificates / weak ciphers.
I have it validating the server certificate, however, I don't think I am doing anything with regards to the weak ciphers. Any advice on that?
A pure RESTful API web service should use the underlying protocol standard features:
For HTTP, the RESTful API should embrace and comply with existing HTTP standard headers, status codes and methods. Adding a new HTTP header violates the REST principles.
RESTful services MUST be STATELESS. Any tricks, such as token based authentication that attempts to remember the state of previous REST requests on the server violates the REST principles.
Bottom-line: For authentication / authorization purposes, you should use HTTP authorization header. And you should add the specific HTTP authorization scheme header in each subsequent request that needs to be authenticated.
I have developed a RESTful web service for the Cisco Prime Performance Manager application. Search Google for the Cisco Prime Performance Manager REST API document that I wrote for that application for more details about RESTFul API compliance - see below. For this application, we have chosen to use the HTTP "Basic" Authorization scheme to authenticate and authorize requests. And obviously, we are using HTTPs to encrypt all the data in transit from the client to the server when using HTTP Authorization.
If token based approaches are not allowed, doesn't that mean the client must store and resend the user's name and password on every request and determine when to invalidate them?
Couple of things - who said I wasn't utilising the authorization header? Token authentication is still stateless, the server remembers nothing beyond the current request - the token is simply used as a ticket to say "*hey, I've been before, he's the proof, so you can skip the authentication step*".