3

I want some advice on my Authorization/Authentication scheme that I will be using for my Web API that I am writing.

Firstly the Authentication :

1) When a user first logs in and they are authorized, I send back a unique token back to the client. This token is used in subsequent API requests in order to authenticate the user.

  • I plan to store this unique token in the users database row and remove it from the database on log out, time out, etc.
  • I was planning to create the token in the format "user/user's IP/timestamp"
  • When the user sends back the token, I will parse out the IP and compare it to the current requester's IP, if that passes I will look up the token in the database and compare the database's token and the client's token.

The part I need help on is the type of encryption to use in creating the token. I was planning to use RSA but the token came out to be like 50+ characters which seems obsessive. What is a better encryption to use so that the token isn't too long?

  • Other things to note is that I will be using SSL to login, register a user, but not to request data so the request will be open text.

2) Secondly is the authorization of the user.

  • When the user first registers, I salt their password with a salt generated from BCrypt and hash that password.
  • I save the salt and the hashed password in the database.
  • When a user wants to log in, I add the hash to the new password and compare it to the hashed password in the database.

Is this a good form of authorization?

Thanks for any help you can provide.

8

First things first: you are not using the correct terminology. This may be problematic if you are looking for documentation.

What you call "authorization" is what the rest of the world calls "authentication". What you call "authentication" is what the rest of the world calls "session management".

Authentication is about making sure that whoever is at the other end of the line really is who he claims to be. Session management is about extending authentication over several successive requests, which collectively constitute a session. Authorization is about deciding what a specific, duly authenticated user should be allowed to see and do.


When using passwords, authentication is defined as follows: a user U will be deemed "authenticated" if he can demonstrate knowledge of a password that only user U knows. This calls for two important comments:

  • Use SSL. The "show the password" authentication method implies, really, showing the password. The client should show it only to the proper server (yours), not to a fake server, and certainly not to the world at large. SSL will provide the necessary server authentication (that's what the server's certificate is about) and encryption. Also, SSL will bind the authentication with whatever is afterwards sent over the link, in particular your token. You won't get any decent security without something similar to SSL, and that's hard to implement and run correctly (especially in a Web context), so use SSL.

  • Don't store passwords themselves. The server does not need to store the password, but only enough to be able to verify a given password. This is where bcrypt is used: you store a password hash on the server. The idea is that if an attacker succeeds in having a look at your database, he won't get all the passwords in one go. That's a second line of defence.

    Password hashing has a lot of theory and involves some configurable slowness and salts. The good news is that bcrypt is a good solution, and most bcrypt implementations already manage the salt. That is, when a salt must be generated, the bcrypt implementation generates one internally and properly; and the salt is encoded in the output. This means that the bcrypt output is a string that you just have to store and read back. See this answer for some details. You should not have to muck with salts directly.


For session management, you want to give to the client, after successful authentication, some fast way to tell to the server that it's him again. Sending back a session token is the normal way of ding that.

The token is a secret key: it is a value whose only important characteristic is that the correct user knows it, but not any other. As such, a good token is a random sequence of bits (128 are enough) generated with a cryptographically secure PRNG. You might want to encode that token into characters with, e.g., Base64, so that it better fits text-based protocols and systems (like Javascript); it does not matter.

Other characteristics are merely extra safety tricks:

  • You may want to restrict the validity of the token in time, so as to force a new authentication regularly or after a given inactivity period. For this, the server must somehow remember the time at which the token was issued or last used.

  • You may want to restrict the token to a given IP address, on the basis that... what exactly ? If you do things properly, then if the token is stolen, then the attacker has enough control on the victim's machine that he may launch the fake commands from the victim's machine itself. On the other hand, normal users may sometimes switch IP addresses (there are ISP who provide dynamic IP addresses and change them occasionally; a user may have a laptop and use the WiFi from whatever restaurant he currently sits in). In any case, if you want to restrict a given session token to a specific IP address, then the server must somehow remember the said IP address.

In both case, the server must remember. On the server side, whenever a session token is created, the server must be able to map back that token value to the proper user identity. So there should be on the server a table, indexed by session token, which points back to the "session data", in particular the user name. An issuance timestamp is handy here to allow an obsolescence policy, in order to prevent that table from growing indefinitely (entries for old, expired tokens can then be removed automatically).


If you have millions of clients then you may want to offload such storage on the client itself. This is a size optimization. Now (and only now) would you envision a special "token format" which encodes some data that your server would like to remember about the session, but cannot or will not actually store in its database. The generic scheme is the following: for a given session, there is some data D which contains everything that the server wants to remember about the session. This includes the user name, the time of issuance or last use, possibly the client's IP address if you really wish to put restrictions at that level... then the token value is an encoding of D || M (concatenation of D and M) where M is a Message Authentication Code (in practice, HMAC) computed over D with a secret key K that the server stores.

The server uses K to generate the token (i.e. compute M). The server also uses K to validate an incoming token. Such a token can be seen like a sort of ID card: after successful authentication, the server grants to the client a temporary ID card, which can be verified by whoever knows the MAC key, i.e. the server itself, without having to maintain some knowledge of all issued cards. From the point of view of the server, things go the following way: if a client connects and sends a token T, then the server validates the MAC part of T, and if that matches, then server knows that T was issued by itself, and its contents are therefore trustworthy.

This scheme has some consequences:

  • Since the client does the storage, the server can manage millions of simultaneous sessions without having to pay the storage cost for it.

  • There is no easy way to enforce an early session close. As long as a client remembers a given token T, and the expiry date (encoded in the D-part of T) is still in the future, then the client can connect back and will be considered authenticated. If you want to be able to force-evict sessions (e.g. as an administrative action) then you will have to maintain some sort of revocation list (a list of seemingly-valid tokens that you decide not to accept nonetheless). Alternatively, by changing the key K on the server, you cancel all existing tokens simultaneously.

  • The client will see D. If you just want to store the user name, his IP address and his date of last authentication, this is not a problem: the user already knows all that. However, generically, if you want to store user-dependent data in the user token D that the user should not be able to see, then you will have to use encryption (which complements but does not replace the MAC).

  • You cannot prevent a user from doing a rollback. If a client connects and sends T1 with data D1, and the server sends back a token T2 with data D2, to be used for subsequent requests, the server has no real way to prevent the client from sending back T1 again. This is unavoidable if the server has a "no user-specific memory" stance.

  • You have a secret key K to manage on the server. Secret keys for HMAC are easy enough to generate (just get 16 bytes from your CSPRNG) but you have to store that key (so that the server does not forget it when it reboots); if the server has several front-ends then all of them must know the same key K; that key K is very valuable to attackers since knowing K allows impersonating every user, so K would have to be well protected (simply storing it in the database would make it an easy prey to basic SQL injection attacks).


Summary: unless you have clear performance issues which require client-side storage of session data, I recommend that you refrain from using a MAC-based kind of scheme. Don't encode user data in the token. Do this:

  • When a user logs in (the client shows the user name and password, and the password verification with the stored bcrypt value succeeds), generate a new token T as a sequence of 16 bytes (128 bits) obtained from a CSPRNG (/dev/urandom, java.security.SecureRandom, System.Security.RNGCryptoServiceProvider... depending on the OS and language). The server creates a new row in the "session table" which maps T to the user data (name, date of last use, IP address...). T is sent back to the client.
  • When a user connects and sends a token T, look it up in the session table to recover the user data. It is sufficient that the client knows T to consider the user as duly authenticated.
  • Prune expired rows from the session table at regular intervals.
  • And do everything above within SSL.
  • Beautiful that all makes a lot of sense thank you! The only part I don't understand is about storing the salt in the database. Don't I need to retrieve the salt when logging in a user? Because the user sends me "password" and I need to salt it with the original salt, hash it, and then check it versus the fully hashed password stored in the database? Isn't the salt required to get the user password back to the hashed version? – Chrisgozd Dec 23 '13 at 16:20
  • Ah disregard my comment above, I just tested it out and it works, that's really cool actually. The other question is how to store the token in the database. Would it be in the same row as the username and password? or a whole different table? If you put it in a different table, how do you relate the token to user's row in the other table? – Chrisgozd Dec 23 '13 at 16:38
2

Re: tokens

  • Just use a long random string as the token. You can make it exactly as long as you want to. Why would you "encrypt" the user ID to use in the token itself? (Or am I misunderstanding something?)
  • You can optionally also store the user IP and token expiration in the token itself, but these could just as well be in separate DB fields, if you're using a database anyway (user_id, token, token_expires_at, ip_address).
  • The token would look like randomString123-$IPADDRESS-$EXPIRATION.
  • When a request comes in, check that the IP is the same, that the expiration time is in the future, and if the token exists in the DB (remember to check the full string, not just the random part!).

Re: SSL

If you're not using SSL for requests, your data and the token are not really secret — you should fix this first!

  • +1 for SSL. If you have an SSL certificate, just SSL everything. It's much easier to secure something where everything is secure-by-default than trying to secure something where some bits are secure and some bits aren't. – Matt Dec 23 '13 at 0:34
  • If I don't include the user id in the token, how do I associate the token with the user when I do the look up in the database? The way I was thinking was to parse out the user id do a "Select token from Users where username=userid"? – Chrisgozd Dec 23 '13 at 16:15
  • @Chrisgozd - SELECT user_id FROM tokens WHERE token = 'THE_TOKEN'. But of course you can add the user id to tokens for faster lookups. (USERID-TOKEN-IPADDR-EXPIRATION) Also, you probably should have a standalone token table so that users can have more than one active token. Even in an API-only use case it's probably required, since many parallel requests can come in at once, and if you expire a token you might break an in-progress client request. – Joel L Dec 23 '13 at 17:24
  • What's a use case for multiple tokens? Querying the API on different computers at the same time? Also if I stored the timeout for the token would it be in the same row as the token in the tokens table? – Chrisgozd Dec 23 '13 at 17:48
  • @Chrisgozd Re: multiple tokens: if I run a service that uses your API, I'll probably want to get a new token before the current one expires — but when I do, I'd still like the original token to work until its expiration time so I don't have to pause making requests while I receive a new token. – Joel L Dec 23 '13 at 23:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.