2

Let's say that during account creation, I already prohibit passwords known to be reused from breaches of other sites, like if it's in the Have I Been Pwned list. But breaches happen all the time, so what if my customers' passwords show up in the list later, after they've already created their account?

I'm of course storing the passwords hashed, salted, and peppered, the whole point of which is to make it intractably expensive to try lots of guesses, such as every newly leaked password. Any information I can think of to store alongside the salted hash to enable checking the newly leaked passwords would also weaken the salted hash. I could wait til they next sign-in and check against the leaked passwords list then, but how can I notify them proactively? I could force re-sign-in by expiring all current sessions, but how can I avoid inconveniencing unaffected customers (especially if that's usually 100% of them)?

What if:

  1. during the sign-in process, the server generates a 160-bit cryptographically random nonce, sends it to the client, and also associates it with the session in ephemeral storage like Memcached
  2. when sending the sign-in request to the server, the client hashes the password with SHA1, XORs it with the nonce, and stores the result locally while forgetting the nonce
  3. when the server learns about a new breach, it pushes the nonce to the client
  4. the client can then XOR the nonce with the encrypted SHA1 and check the SHA1 hash with Have I Been Pwned or whatever

Notes:

  • the nonce never touches disk/durable storage, anywhere
  • the customer's password never touches disk/durable storage, anywhere (nor any value from which the password can be easily extracted, e.g. unsalted SHA1)
  • without the nonce, the encrypted SHA1 is useless because the XOR pad/one-time pad encryption is cryptographically perfect, so no vulnerability to XSS or compromise of the client's computer
  • the server can expire the nonce after a while, and/or limit how many such nonces it keeps around per customer account, either of which render the encrypted SHA1 irreversibly useless

If my systems were completely pwned, of course, then next time the client updates it could include malicious code to combine the nonce with encrypted SHA1 and exfiltrate the cleartext SHA1 the attacker's server, but that seems like a much higher barrier than my database dumps being compromised.

One tiny way to potentially strengthen this that I can think of is that it might be possible, instead of storing the full SHA1 hash, to just store a prefix of enough bits such that the relatively small number of newly leaked passwords would collide with only very few of my customer's SHA1 prefixes, but few enough bits that it would only filter a dictionary attack down to dozens or hundreds of guesses, which is sufficiently many to not be usable on a properly ratelimited online service. I don't think this mitigation is very useful, though, even if such a number of bits existed, because it's difficult to imagine the attacker not having the Argon2-hashed password and salt, which would be very easy to crack if guesses have been narrowed to hundreds or even thousands.

14
  • 1
    In step 2, are you also sending the password to the server "plaintext over HTTPS"? When you say "without the nonce, the value in localStorage is useless" that is true, but the server also sends out the nonce periodically, so kinda moot? This sounds like security theatre, you're more likely to inadvertently introduce a vulnerability than meaningfully improve users' security. Look at MFA for boosting login security.
    – paj28
    Apr 17, 2020 at 5:20
  • @paj28 Re step 2: sure, why not, that's considered safe and isn't made less safe by any of this. Re sending nonce periodically: localStorage is stored in cleartext in well-known, user-readable files, indefinitely. If the password were put there unencrypted, it could be compromised if the laptop were stolen years later. Only showing up transiently in memory and then being deleted seems like a major defense-in-depth hurdle to me? Apr 29, 2020 at 6:18
  • @paj28 MFA is great, but can only protect access to a service. Passwords are irreplaceable for protecting data-at-rest, for example Apr 29, 2020 at 6:25
  • 1
    @paj28 Where is that token stored? If it's stored on the server, then it provides no additional protection against password reuse on top of end-to-end encryption. If it's stored on the client, does the user have to migrate it from device to device? May 5, 2020 at 19:55
  • 1
    Sorry, you are right, a 5 digit code is only useful with a lockout policy. So either an online service or something like a TPM. For a longer code; some password managers do sync; some people keep their password manager on their phone. And yes, I agree it's a good idea to try to make a login system that makes it harder for a user to mess up. Ok, I feel I've shared a few ideas but none of them have really helped you. Been an interesting conversation. I'm not going to try to help you further, but I would be interested to see what your final design ends up being.
    – paj28
    May 11, 2020 at 4:50

2 Answers 2

1

I think what you are pursuing is quite ambitious but it is overkill. In my opinion you should simply:

  • enforce a minimum level of password complexity
  • add two-factor authentication (2FA) - this will make up for weak or compromised passwords
  • if possible make 2FA mandatory or try to enroll as many users as possible
  • devote your free time toward tightening and auditing your systems - you may have some vulnerabilities present, that could nullify all your security efforts

So the answer is not to rely on the password alone for authentication purposes.

Checking the password supplied at signup against pwnlists is a good idea. Do it offline, that is against password lists that are stored on a local drive. Do not submit the password to an online service for verification.
You can repeat this check whenever the user changes their password.

Regarding implementation of 2FA you have a number of options. It would be good to offer some choice, for example many users could be satisfied with a Google authenticator but others may more comfortable with an alternative solution.

SMS is not considered safe these days but it depends on the value of the assets you are trying to protect.

1
  • Yes, I agree that 2FA is more important, and that checking against pwnlists just during login is more clearly worth the effort. However, 2FA has the limitation of only protecting a service; end-to-end encryption of data ultimately relies on the strength of the password, and can't rely on any other factor. That's why I think it's worth thinking about schemes like this to strengthen the security of passwords. May 7, 2020 at 0:53
0

You don't need any of that. You already have the hashed, salted password, and you have the salt. If the service you use for leaked passwords provides you with the plaintext passwords, you can employ a "dictionary attack" against all your accounts, and see if any leaked password matches your clients.

The HIBP API service allows you to search for partial SHA-1 hashes, so you could add a field on your table to store the 5 first chars of the SHA-1 hash of the password. Periodically, you probe HIBP for the hashes and see if any of your clients got a leaked password.

I got the top 96016 password on the RockYou password dump, ran sha1sum on them. I retrieved only the first 5 chars (like HIBP API), sorted, and counted unique entries. Of that universe, I got 91753 unique values. Less than 4% were duplicates. Only one entry had 6 copies. 110 got between 3 and 6. So if this system detects that your password probably leaked, you have at least 95% chance it really leaked.

11
  • HIBP does not provide access to plaintext credentials and I am not aware of a service that does. I believe your second suggestion doesn't work because you would need to store the full SHA-1 hash in order to compare it with the results of the partial search returned by HIBP. Storing an unsalted SHA-1 hash severely compromises the security of the password storing scheme.
    – trallgorm
    May 5, 2020 at 21:11
  • 1
    You don't need the full hash, only the 5 first chars (that's what HIBP asks for). If the partial hash matches, you tell the user that his password probably have leaked.
    – ThoriumBR
    May 5, 2020 at 21:54
  • 1
    If the partial hash matches but you don't have the original hash, you don't know whether it leaked or not. For example, let's say "apple" and "orange" both hash to something with the same first 5 chars. You hit the API and it returns a breached password, the SHA1 of "apple". But your user had "orange" as their password. Without having the original hash you can't tell them apart, and thus can't tell the user if they have been breached. You can read more about how it works here: troyhunt.com/ive-just-launched-pwned-passwords-version-2
    – trallgorm
    May 6, 2020 at 14:07
  • That's why I wrote "probably"...
    – ThoriumBR
    May 6, 2020 at 14:15
  • 1
    @ThoriumBR Again, please read the HIBP API documentation, it does not work the way you think it works. It literally says in the link you provided that it will ALWAYS return at least a few hashes no matter what prefix you supply. Feel free to test it if you would like. Generate any random password, submit the prefix to HIBP and according to your scheme it will be marked as breached. The OP is correct in his comment, the rockyou dump is very small subset of HIBPs password list.
    – trallgorm
    May 7, 2020 at 2:18

You must log in to answer this question.

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