11

I'm working on a (non high security) project that currently isn't live but might go live at some point.

We have a REST API (implemented using Restlet and Neo4j) running on a server and an Android client app.

To keep things stateless, we need to send auth information together with each request. We decided to use a MAC-based approach: We sign a set of header values as well as the entity body of each HTTP request using the HMAC-SHA256 algorithm. We then put that hash together with the username in the Authorization header of the request.

For the HMAC secret, we use a password that is chosen by the user. To make things more secure, we hash the password before using it and storing it in the client.

Of course, in order for the server to be able to validate the HMAC-SHA256 header, we need to transmit the secret once. But as this only happens a single time, it's not that much of a security risk.

The problem with this setup is the secure storage of the user passwords. I know that passwords should never be hashed without using a salt to prevent rainbow table attacks. But when I generate a secure/random salt, a client that logs a user in for the first time has no way to get access to that salt in order to generate the correct hash.

What would be the best decision in this case? Should I simply store the passwords using a "conventional" hashing algorithm like SHA256 without using a salt? I guess that would be very negligent. I could also use a more secure algorithm like bcrypt or PBKDF2 with a "dummy salt" like the username a MD5 hash of the username and a higher iteration count. Even though the salt is known, it would be very slow and painful to generate rainbow tables.

Would bcrypt with a "security-by-obscurity" salt be secure enough? Or is the entire setup insecure and should be exchanged with another authentication method?

3 Answers 3

5

The most important thing you should be doing is: use SSL. Use HTTPS, not HTTP.

Justification:

  • If you use SSL, your approach is reasonable.

    • Tactical detail: I suggest using PBKDF2 with a large iteration count to generate the HMAC key from the password. You can use a fixed value (or a hash of the username) as the salt.

    • This will admittedly have some weaknesses: since you store a hash of the password on the client device, someone who gains access to the mobile device can use dictionary attacks to try to recover the password, and they might or might not be successful, depending upon how strong the password is. Also, anyone who gains access to the mobile device can learn the crypto key, which is all that is needed to submit authenticated HTTP requests. In many settings, this weakness might be an acceptable risk.

  • If you don't use SSL, your scheme will probably have major security problems. Anyone who can sniff the wire will be able to learn a hash of the password, and if the password is not strong, they may be able to recover the user's password using dictionary search.


If you want even better security, then here is a slightly stronger approach:

  • Use SSL throughout. Don't mess around with unencrypted HTTP; use HTTPS for everything.

  • Use a full-strength crypto key. Have the server generate a full-strength 128-bit cryptographically-random crypto key for use with HMAC. The server can store this key.

    • The first time the client connects, it will need to learn this crypto key. To do that, have a special request where the client supplies its username and password, and receives the crypto key in response if the password is correct. (The server can rate-limit these requests, to deter dictionary attacks.) When the client receives the crypto key, it stores it locally.

    • For every subsequent request, the client can look up its locally-stored copy of the crypto key and use that to compute the HMAC.

    • If the user logs onto the web site from a new device, no problem, that device just goes through the initialization step mentioned above to retrieve the crypto key, then everything continues to work.

This avoids the need to store the user's password or a hash of the user's password on the end device. It also prevents dictionary-search attacks against the crypto key; since the crypto key is a fully-random value that's chosen randomly independent of the password, off-line dictionary search attacks against the password are not going to be successful at retrieving the key. However, it does retain the weakness that if a bad guy gets their hands on the client device, they will be able to learn the crypto key and thus issue forged requests. I suspect that's about as good as you can do, given your goals.

4

I'm not sure if i understand your problem entirely.

The salt is usually encoded in the bcrypt string representation.

However using the username as salt probably won't work as bcrypt expects exactly 16 bytes as salt. So you'd need to transform the username before. What i don't understand is, why can't both sides have the salt? As you said, the salt is only to prevent rainbow table attacks, so you can probably even transmit it without that being a security problem.

5
  • The salt is encoded in the bcrypt string, yes. But the problem is that that string is only stored on the server side. If the user buys a new smartphone, he enters his password in it, and then a hash has to be calculated from that password that matches the one stored on the server. Commented May 17, 2012 at 22:58
  • then you probably need to throw in a "static" salt. i think transforming the username to a 16 byte value (and using that as a salt) shouldn't be a problem. because then you can generate a rainbowtable on a per username basis, which is entirely useless, since you could just as well brute-fore.
    – lawl0r
    Commented May 17, 2012 at 23:03
  • Thanks. I didn't know about the 16 byte requirement. In that case, a MD5 of the username should be sufficiently secure to prevent rainbow table attacks. Even though that would be probably still more common than stuff like repeating the username several times and using the first 16 characters of that string... Commented May 17, 2012 at 23:06
  • just add a static salt to the username, so other who would have exactly the same implementation can have a different hash. then you can't even rainbowtable for multiple sites with the same username and algorhithm.
    – lawl0r
    Commented May 17, 2012 at 23:16
  • Apparently the salt isn't just a 16 byte string: stackoverflow.com/questions/8869367/… I ended up using "$2$10$" + sha256val.substring(0,22) for the moment. Commented May 18, 2012 at 1:21
1

If you use a hashed password as a HMAC key, then your hash becomes the shared secret. Hashing it (with or without salt) will protect the password in case it is used somewhere else, but it will not protect your service, as the hash can be used directly.

I think it is best to consider the hmac key a credential, a token. And you cant store those really secure. Of course you should put them in secure location anyway.

Instead of "sending password one time" I would recommend you use something similiar to OAUTH tokens to derive a temporary secret, or you can make it simple and generate the "access keys" on the server and allow the user to copy them (this is for example what Amazon AWS does). If this is a random string there is no danger of leaking user passwords (only, again the credential of course).

An alternative would be to use something like SRP, it will result in a shared pre-master secret which can be used for your request signing key.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .