14

We have a website where users need to log in to access privileged information. Obviously we are using SSL, but I also want to avoid plaintext passwords from accidently ending up in server logs, or wandering eyes of administrators. Therefore, I want to implement client-side hashing using Javascript.

Furthermore, I want to avoid revealing pairs of users with identical passwords, thus use a user-specific salt. Lastly, I would like to add a random challenge (aka nonce) into the mix to ensure an eavesdropper* can not use the final hash as the new "password". I will make an additional roundtrip to the webserver to obtain the salt and challenge before starting the password hashing. *) Yes, we are using SSL, but as a defense in depth I would like to make the implementation as solid as possible.

Especially the challenge is challenging. The only feasible implementation I know of, is first calculating the final password hash (using bcrypt, scrypt or PBKDF2) and then do one additional sha256_hmac round using the challenge. On the server-side we fetch the password hash from the database and perform the same additional sha256_hmac and compare the result.

However, the above would imply that the full password hash calculation must be done client-side in Javascript, because hashing in the challenge should always be the last step. On a legacy(?) iPhone 3G, only 6 bcrypt rounds, or 5000 PBKDF2-SHA256 rounds can be done within 2 seconds which is the maximum delay we can tolerate.

Question #1: Am I correct that nowadays bcrypt should be at least 12 rounds, and PBKDF2-SHA256 at least 5000 rounds? So I should choose the 5000 rounds of PBKDF2-SHA256?

Question #2: Do there exist challenge-response mechanisms that can operate earlier in the process (e.g. on a once hashed password), so I can leave the bcrypt/PBKDF2 hardening to the webserver?

Question #3: Assuming no such challenge-response mechanism exists, is it better to abandon my challenge-response requirement and only apply a user-specific salt client-side and do thorough hardening server side? Would that result in an overall stronger system than using a challenge-response mechanism and less rounds?

EDIT: To summarize the long trail of comments below about the security of client-side hashing in general: the general opinion seems to be that the added complexity is not worth it and it is better to put the effort in auditing the server-side code and limiting server-side access for untrusted staff.

Jason Smith
  • 1,551
  • 2
  • 11
  • 12
  • When I implemented somethnig similar before I used hash(hash(username, password), session_id) - which avoids some of the complexity in your solution - the username is effectively a salt for the paword hash - and this value is stored serverside, and the sesison id is already available at the client - although using a value independent of the session id might be an idea if you erstrict javascript access to the session cookie. – symcbean Jul 12 '12 at 13:38
  • You didn't mention on your question what serverside language you are using but you mentioned PHP in a comment below. There is now an excellent php+js demo of SRP by Ruslan Zavacky over on github at https://github.com/RuslanZavacky/srp-6a-demo – simbo1905 Jun 05 '14 at 13:41

4 Answers4

11

I think you're wasting your time and adding needless complexity. I don't think the reasons you give are sufficient to warrant this kind of client-side password hashing mechanism.

Instead, I suggest keeping it simple. Send the password over a SSL-encrypted link. When it comes to security, simple is good. Needless complexity is the enemy of security, because it introduces the opportunity for bugs and mistakes.

Save your energy for other security tasks that will address more realistic threats. There's a limited amount of time and energy and budget one can spend on improving security; best to spend it on the places that will yield the greatest bang for the buck (the greatest improvement in security, given the investment). I think you'll find there are other ways you could spend your hardening your system that will improve security more.

If you're worried about whether the server-side code is handling the password appropriately, then review that code carefully: it should be contained to within a very small chunk of code. This should take care of risks like, e.g., the server logging cleartext passwords: you just read every line of code that's involved with password checking or that reads the password, make sure the first thing it does is to hash the password appropriately, and make sure that all it does is check the password for validity and return true or false. If your system is well-designed, this should be pretty straightforward to verify through code review. If the code isn't well-designed, maybe now would be a good time to fix that.

D.W.
  • 98,420
  • 30
  • 267
  • 572
  • 1
    What about this simple attack scenario: the attacker manages to sniff all traffic going into your server for a few months and then they manage to compromise the server and copy all useful data, including the private key. Hopefully you detect the intrusion quickly and cut off their access but they still have both the encrypted traffic for the past several months and the private key used to protect that traffic. So, if hashing is being done on the server-side now they can get all plain-text passwords, which would not have been the case if you hashed on the client-side. – resistor Jul 16 '12 at 01:51
  • 1
    @resistor, it is possible in principle, but in practice this is a pretty uncommon scenario. It is rare for bad guys to be in a position to log all of the traffic going to your server over a period of months, or for them to have an incentive to do so. If this is a major concern for you, you could look at forward-secure ciphersuites for SSL (something that is easy to set up with a single line of configuration), but I suspect for most sites it is not worth worrying about. And in the unlikely event that this situation arises, you do still have options: e.g., resetting all your users' passwords. – D.W. Jul 16 '12 at 02:16
  • @resistor - How exactly are they going to sniff encrypted traffic? If they have physical server and/or the ability to instal forge certificates to perform a MITM attack against your users then you have bigger security concerns – Ramhound Jul 17 '12 at 18:22
  • 1
    @D.W. thanks, that is indeed a better way to address this. – resistor Jul 31 '12 at 23:09
  • @Ramhound They can sniff encrypted traffic if they can get access to a router that carries some/most of the traffic to the server. This is not that unreasonable for governments or other entities with enough resources and is one reason why SSL should be used in the first place. And this is not related to MITM attacks or installing forged certificates, just passive sniffing and then eventually getting a key that can decrypt all of the previously captured traffic. – resistor Jul 31 '12 at 23:10
  • 1
    Agreed. Definitely rely on SSL to protect passwords coming from the client-side. 1) Doing otherwise would require a large code payload to implement all the encryption technology you'd need to pull it off. 2) You can't trust anything that happens on the client-side anyway. Maybe you can cover a MITM case, but what about a keylogger? 3) You know server CPU power. Worry about this stuff, first: http://ericleads.com/2013/08/authentication-from-programming-javascript-applications/ – Eric Elliott Oct 06 '13 at 20:08
6

If you have not done so already, you may want to look into SRP. It should meet all of your requirements, if you want a challenge/response type system.

http://en.wikipedia.org/wiki/Secure_Remote_Password_protocol

broadway
  • 444
  • 2
  • 3
  • Thanks for the suggestion. This looks indeed useful. Unfortunately, I was only able to find two Javascript implementations (srp-js, which appears to be abandoned/incomplete), and Clipperz (which is bulky and requires a 430 KB Javascript download each time a user tries to log in). Native support by the major browsers is currently limited to Firefox. I read [this](http://security.stackexchange.com/questions/12850) Nonetheless does this seem the way to go. I keep looking for a lightweight SRP implementation in Javascript + PHP. – Jason Smith Jul 12 '12 at 22:04
  • I studied the protocol. Am I right that one still needs to calculate the PBKDF2 client-side? If so, using SRP only helps the challenge-response, but does nothing to improve the key stretching. – Jason Smith Jul 13 '12 at 17:31
  • Sorry for the sloppy wording. Let me rephrase that: SRP looks like the perfect solution for the challenge-response problem, but still requires client-side password hashing. Thus I still have the dilemma of choosing between less hashing rounds client-side, or skip the challenge/response and do all hashing rounds server-side. – Jason Smith Jul 14 '12 at 11:07
  • This has been done by done with [Ruslan Zavacky's spr-6a-demo][1] which uses PHP on the server and runs the SRP serverside algorithm in PHP. [1]: https://github.com/RuslanZavacky/srp-6a-demo – simbo1905 Jun 05 '14 at 13:39
4

I would be wary of any home brewed protocol. I would also be wary of any client-side hashing based authentication protocol. Your best bet is to study NTLM Authentication and the LARGE NUMBER of attacks against this protocol. Such as the NTLM weak nonce vulnerability.

(SRP isn't bad, SRP-TLS looks pretty cool.)

rook
  • 46,916
  • 10
  • 92
  • 181
  • SRP is indeed very cool. However, does not have adequate support on browsers to be a serious candidate for deployment on web sites today (alas!). – D.W. Jul 16 '12 at 02:16
1
  1. No, you're still too small. OWASP Password Storage Cheat Sheet recommends 64,000 PBKDF2 iterations in 2012 and doubling them every 2 years (https://www.owasp.org/index.php/Password_Storage_Cheat_Sheet).

  2. See the other answers above.

  3. You do have the option of doing some hashing client-side (say, 2,000 PBKDF2_SHA-1 rounds), and then once the result of that gets to the server, run it through another large set of hashing (say, 300,000 rounds of PBKDF2_SHA-512), and compare that final value.

Your database stores only the final double-PBKDF2 value.

Your web log files and/or possibly weak SSL sessions use the intermediate single-PBKDF2 value. While that intermediate value is certainly much, much weaker than the final value, it's still quite a lot stronger than cleartext.

Note that you also need to check passwords users propose when changing their password/selecting a new passwords against a normal cracking dictionary with some rules (lowercase both to eliminate case games, add numbers from 1 to 1000 after the word, add dates after a word, basic 1337 speak translations, etc.) to prevent the "strong" password "P@$$w0rd" (upper case, lower case, symbols, and numbers - 8 characters long, it must be really strong!) from showing up.

Scott Pack
  • 15,167
  • 5
  • 61
  • 91
  • 3
    The more arbitrary complexity rules you add to a password, the more likely it is for a user to just write it down on a sticky note, which is why I think enforcing too many rules is a terrible idea. – Null Nov 02 '12 at 20:32