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)?
- 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
- 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
- when the server learns about a new breach, it pushes the nonce to the client
- the client can then XOR the nonce with the encrypted SHA1 and check the SHA1 hash with Have I Been Pwned or whatever
- 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.