20

I just encountered a password hashing / password reset scheme that I've never seen before. I'm skeptical, but can't think of a concrete reason why this is bad.

The scheme for an account creation / password reset goes like this:

1) User types password "FuzzyCat" into a client-side app.

2) Client-side app hashes the password hash("FuzzyCat") -> "99476bb..." (possibly after requesting the hashing policy - salt, which hash function, etc - from the server), and passes the hash to the server.

3) Server stores "99476bb..." in the database as the password hash for that user.

4) When the user comes to log in next, they enter "FuzzyCat", the server hashes it and compares it to "99476bb..." in the database.


The use-case where I saw this is that accounts are initially created by an automation script as part of a several hour bulk process, and we would rather not have the plaintext passwords floating around in memory / on disk during that time. All subsequent logins by the user will be directly to the service over a secure channel (note: not https, I mean "sign the logbook to get physical access to the room" type of secure channel).

To address comments, the reason we don't trust the automation script is that it is written in a language with immutable strings and garbage collection, so any memory containing passwords will be returned to the OS un-zeroized - which does not meet our internal policies for password handling. So yes, the main concern is a passive MitM.


Question: What possible vulnerabilities / problems might there be with this scheme?

The only one I can think of is that the server needs to rely on the client being honest and following the hashing policy, potentially allowing users to put weak hashes in the db. This is not a big deal because at login, the server will hash their password with the real hashing policy and the hashes won't match, ergo no login, no breach.

As far as I can tell, there's no risk in an attacker getting the hash because it does not help them to log in. Am I missing anything?

  • 5
    Sounds about OK, but why is the client hashing the password in first place? – Ayesh K Feb 15 '17 at 22:28
  • 1
    Is it possible that the server additionally hashes the hash received from the user? – GrawCube Feb 16 '17 at 2:31
  • 1
    Used for 'password reset'? How so? So the user can send a new hash to override the old one? How are they authenticated when they do that? Possible implementation error with SQL injection where the server takes whatever the client sent and puts it in the database, instead of the always-fixed-length hash output. – TessellatingHeckler Feb 16 '17 at 6:41
  • User types their password into {what, exactly}? How does the hash get to the automation script? How does 'retrieving the salt' from the server work with the automation script? If the user can talk to the server, can't they create the account directly? If not, how does the client get the hash policy from the server? Could the automation script change the server reply and tell the client "the hash is ROT13", then find the passwords, then re-hash them properly for insertion into the DB? – TessellatingHeckler Feb 16 '17 at 6:53
  • Why don't you trust the script with the plaintext passwords? They're not being reused elsewhere, are they? Because if the account is super important, and the script has a malicious actor... script replaces all user supplied hashes with hash('123') and whoever controls the script gets free run into the accounts for (minutes / hours) before the user notices their password doesn't work and tells someone...? – TessellatingHeckler Feb 16 '17 at 6:55
18

I agree with @Xiong Chiamiov, there isn't anything inherently wrong with this approach.

On the other hand, it doesn't provide much benefit to the standard approach (all hashing is done server-side), and there is definitely some risk in disclosing the hash to untrusted entities.

If the original client isn't trusted, this obviously does nothing, as the client could just log the entered password.

If the original connection between client and server isn't trusted, this only helps a little. A passive man in the middle still gained the hash and can try to crack it. An active man in the middle could change the hash to gain access, or could change the reply to the hashing policy to force a weak hash to gain the password (or force no hashing at all; or just inject Javascript or similar to read out the entered password).

So the only sensible use-case is limited mitigation against a passive man in the middle on initial signup. If I would encounter this, I would check how untrusted the initial signup really is, and if there isn't a more sensible approach than disclosing the hash.

  • 4
    "A passive man in the middle still gained the hash and can try to crack it." - that sounds exactly like the benefit. The MiM does not gain the password itself in the signup/reset process, he needs to crack the hash first. So if the login with the real password happens via a different (not observable) channel, this sounds like a better scheme than sending the plain password in the insecure signup/reset channel. – Bergi Feb 16 '17 at 16:11
  • I agree with everything in this answer, except possibly this: "and there is definitely some risk in disclosing the hash to untrusted entities." How would it be "disclosed", and what's the risk? – TTT Feb 17 '17 at 16:58
21

The issue that usually comes up when talking about client-side hashing is that an attacker who has breached the database can merely pass the hashes to the server to authenticate. However, this scheme doesn't allow use of the hash for authentication, just the initial account creation, so it doesn't fall into that trap.

I've seen this sort of thing used in the context of Apache htpasswd files; users generate a digest and send it on a relatively insecure channel (e.g. email) to the systems administrator, who then adds it to the server configuration. While a system involving basic or digest auth isn't the pinnacle of computer security, this shows that it's not an entirely novel approach, and has had at least some attention.

Given the description as you put it, I don't see any obvious issues. There can of course be implementation issues, like a bug that allows feeding the hash instead of the password for authentication. And since the initial connection isn't trusted, it's probably a bit easier for an attacker to obtain a password hash (that they can then try to crack or generate a collision for). But those aren't issues with the scheme itself, or major issues.

2

Two nits

possibly after requesting the hashing policy - salt, which hash function, etc - from the server

I am not clear if this mechanism uses a per-user salt or a global salt. If it uses a global salt, it is vulnerable to a rainbow attack.

Server stores "99476bb..." in the database as the password hash for that user.

Because the hash was generated outside of the server, there is no way for the server to enforce any password complexity or length rules. They would have to be enforced by the app. Also, there is no way to check for password re-use.

  • Why would you even care about password re-use? How would you test it? How is it useful in any way to check for password re-use? If you're talking about password re-use by the client, then that may be useful – Ismael Miguel Feb 16 '17 at 9:14
  • 4
    @IsmaelMiguel If the system requires passwords to periodically be changed (which is recently discouraged by NIST), then it has often been desirable to check that the newly entered password doesn't match any of your previous X passwords. If the initial hashing is done on the server, this is trivial to check, simply attempt to validate it against the previous hashes. If the initial hashing is done on the client, you can do no such thing. – Thorbear Feb 16 '17 at 12:06
  • 1
    @Thorbear Why you can't just compare the client hashes? Are you assuming the client can change how it hashes its passwords? I don't think a client should allow that because then users will need to remember passwords and hashing settings. – Gustavo Rodrigues Feb 16 '17 at 12:43
  • 4
    @GustavoRodrigues Any recommended password hashing algorithm will either take as input, or generate on its own, a random salt. The same password, using the same procedure, should produce different hashes. If the server doesn't have access to the plaintext at the point of registration, it can't compare the input to previous hashes (the server could still detect it the first time the user attempted to log in, but it is a bit late at that point). – Thorbear Feb 16 '17 at 13:34
  • 1
    @Thorbear - Wow did I muddy that up. Somehow I crossed the normal way of preventing reusing passwords with this answer. Sorry about that. It sounds like the server may generate the salt, but you're right, that still doesn't help unless it always uses the same salt (which isn't recommended). I must have had a senior moment. Or more like a senior day...thx for setting me straight. – TTT Feb 18 '17 at 14:17
2

I don't think the proposed scheme adds any additional risk, but I'm not sure it is actually reducing any either. There seems to be one bit of crucial information missing. Under this scheme, how do you initially vet the client to know they are who you think they are before you allow them to set the hash? This is the real challenge - there are a number of different ways you can communicate a password set/change over the wire to prevent MitM attacks, but when you want to allow remote setup of any form of authentication process, the issue is in vetting the remote user.

I'm also not convinced the policy which is causing you to have to consider this alternative is of any real benefit except to make auditors feel better. Lets face it, if your risk is harvesting of password information from OS memory, the real problem is adequate controls over access to the OS and that memory, not what is in the memory. If someone has that level of access, then they can likely compromise your base authentication process anyway. All the policy is really doing is adding complexity, which will likely create other problems.

  • The answer is that my use-case is actually pretty narrow: there is no "wire", all of this is local on the physical keyboard of the server. The authentication mechanism is to check their ID badge before allowing them into the server room. So, I have to agree with your second point: worrying about an attacker having access to a memory dump is probably more of a philosophical debate than a practical one. Point taken. – Mike Ounsworth Feb 16 '17 at 20:37
  • 1
    So, if all interaction is via the keyboard directly connected, then your 'client' for initial hashing is really your server? This would mean the hashing is still done on the server, just with different language to the bulk registration code i.e. not immutable language so memory concerns don't exist as you are able to zero password variables afterwards? – Tim X Feb 16 '17 at 21:12
  • Uhh wat? I really regret that I even attempted to explain my use case at all. Yes everything is local to the server box. The application deployment and initialization is orchestrated by a scripting language that I don't have very much control over. User accounts are created (and passwords set) as part of this process, via the scripting language as users don't want to stand there for the several hour deployment, waiting for a prompt. Once the application is deployed and running, subsequent logins are handled directly to the application via keyboard, passwords are handled properly, etc. – Mike Ounsworth Feb 17 '17 at 3:37
  • Sorry, I didn't mean to make this hard. I just found your statement "all of this is local on the physical keyboard of the server". this being the case, the only MitM attack possible means your server is compromised, in which case the game is over already. My original interpretation was that steps 1 .. 3 were initiated remotely (so MitM is a concern) and I wasn't sure how you did the initial authn to allow user to set initial hash. Based on everything being local, the scheme is just adding complexity. – Tim X Feb 17 '17 at 8:42
  • Makes sense, thanks. I agree that the attack surface is very very small, but policy says "memory containing passwords must never be returned to the OS un-zeroized". So I dance. Sigh. – Mike Ounsworth Feb 17 '17 at 16:07

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.