Take the 2-minute tour ×
Information Security Stack Exchange is a question and answer site for Information security professionals. It's 100% free, no registration required.

When storing user's passwords that you need to verify against (but not use as plaintext) the current state of the art is:

  1. Hash the password
  2. Use a salt
  3. Use a slow hash function - bcrypt, scrypt, etc.

This provides the best possible protection against an attacker who has stolen the password database. It does not solve other issues like phishing, malware, password re-use, etc.

But there is one remaining problem: the slow hash function can allow a denial of service attack. A single request burns a lot of CPU, which makes a DOS possible with a relatively small number of concurrent requests, making it difficult to use defences like IP throttling.

Given the improving performance of JavaScript in browsers, it may now make sense to do this in the browser. I'm assuming here that the site is using SSL, so the JavaScript is delivered securely. If you're not using SSL, then using a slow hash function is unlikely to be a priority for you. There is at least one JavaScript implementation of bcrypt. But using this in a simple way would introduce two problems:

  1. The client needs to fetch the salt from the server. This introduces latency on login and, unless care is taken, can reveal whether a particular user account exists.
  2. If hashing is done purely on the client then the benefits of storing hashes are lost. At attacker who has stolen the password hashes can simply login using the hashes.

However, I think there are acceptable solutions to both of those problems:

  1. The salt can be generated as hash(server_salt + user_name) - where server_salt is a random number that is unique to the server, public, and the same for all users. The resulting hash appears to have the required properties of a salt.
  2. The server should do a single, fast, hash operation on the hash it receives. As an example: the server stores SHA-256(bcrypt(salt, password)). The client sends bcrypt(password) then the server applies SHA-256 and checks the hash. This does NOT allow an attacker to conduct a fast offline brute force attack. They can do a fast brute force of SHA-256(password) because password has a limited amount of entropy - 2^50 or 2^60 or so. But a 128-bit bcrypt(password) has entropy or 2^128, so they cannot readily brute force it.

So, is this a reasonable and secure approach?

I am aware of the general advice to "don't roll your own crypto". However, in this case, I am attempting to solve a problem that is not solved by off-the-shelf crypto. For some basic credibility, this has been looked at by John Steven (a recognised expert in the field) with positive outcome from a "brief" analysis.

share|improve this question

2 Answers 2

up vote 15 down vote accepted

Using servername+username as salt (or a hash thereof) is not ideal, in that it leads to salt reuse when you change your password (since you keep your name and still talk to the same server). Another method is to obtain the salt from the server as a preparatory step; this implies an extra client-server roundtrip, and also means that the server would find it more difficult to hide whether a given user name exists or not (but it does not matter much in practice).

What you describe is a well-known idea, usually called server relief, which can be applied to just any password hashing function in just the way you describe:

  • Client obtains (or computes) the salt.
  • Client computes the slow salted hash value V and sends it as password.
  • Server stores h(V) for some fast hash function h, and uses it to verify the V value from the client.

This is safe. The main drawback is that the slow hashing will go at the speed of the client, so the number of iterations may have to be lowered, possibly considerably so if the client is computationally feeble -- as is the case for anything involving Javascript. If the system must work with a browser on a not-so-recent smartphone, then the iteration count will have to be 10 to 100 times smaller than what could be done on the server, implying a corresponding reduction in resistance to brute force (in case the attacker steals the hash values stored on the server).

So that's a trade-off: since the busy server offloads work on clients, it can use a higher iteration count without itself drowning under the load; but since the clients are weak, the iteration count must be lowered, usually much more than it was increased thanks to the offloading; this means that the construction is not worth the effort. Usually.


Of course, if the clients are computationally strong, e.g. this is a native-code application on a gaming machine, then server relief is a good idea and will look like the way you describe it.

Another possibility is offloading the work to another, third-party system, e.g. other clients (already connected): this can be done securely if the hash function includes the necessary mathematical structures. Such offloading is know as delegation. To my knowledge, delegation is offered by only a single password hashing function, called Makwa (see the specification), one of the candidates of the current Password Hashing Competition.

share|improve this answer
    
Thanks...I had no idea this was called "server relief" and that it's an established idea. All the online references about it seem to be related to this PHC; was the idea around beforehand? (perhaps it had a different name?) Anywya, the PHC is very interesting! –  paj28 May 25 at 20:40
1  
Additionally, you need to ge careful that you are not just replacing the password authentication with hash-of-the-password authentication (the hash just becomes the new password). In such a case, you lose the protection that the hash gives you against a lost password database. –  atk May 26 at 1:18
1  
The hashing performance of javascript can be pretty good these days, well within 2x of native code when using AsmJS or Chrome. A hash tuned for 32-bit is a must. It depends on which clients must be supported. –  user239558 May 26 at 8:04
    
Only twice slower Javascript than native code for a hash function ? I'd love to see such benchmarks (this would even be faster than Java or C#). Do you have any reference ? –  Tom Leek May 26 at 20:51
    
This is my private implementation and not open source yet, sorry. –  user239558 May 27 at 9:12

I think the biggest concern about this approach is that it moves a security procedure you've determined is important off of your servers and on to the web browsers of users. While normally everything should work as expected you have no real way to verify that the password was actually hashed with bcrypt before it is sent to you. So if the client-side JavaScript is changed you may still end up with only a SHA256 hash of "123456" on your server.

Sure, you could check the format of data sent from the client to make sure it looks like it was hashed with bcrypt, but you don't know for sure that your desired operation completed.

That's not an ideal security model, but despite that I think the threats to this system are still fairly manageable. And you would be ahead of those sites who either don't hash or hash using plain MD5.

share|improve this answer
    
If the client doesn't calculate the hash, then the result will be wrong. The server still needs to calculate the hash when changing the password, but not to verify it. –  Gilles May 26 at 7:26
    
If the server does generate the bcrypt hash directly upon password change then you're correct. I didn't assume that since the question focuses on the client doing the bcrypt work. Without the server stepping in to do that a non-compliant client could submit a password change value without running it through bcrypt and subsequent authentications won't require it either. –  PwdRsch May 26 at 16:59

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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