In an article on password hashing security, Anthony Ferrara suggests using bcrypt to hash passwords and then using a "strong cipher (AES-128-CBC) with a key rotation policy".

This seems like a good suggestion which would help to counter ASIC attacks on bcrypted passwords. However, this could be circumvented if the secret key (or key rotation system) was compromised along with the password hashes.

The Question:

  1. is the suggestion to cipher the password hash a good one, or could it actually weaken security?

  2. would it be appropriate to use a public key cryptography algorithm like RSA, generate a public and private key pair, and then 'throw away' the private key to prevent the ciphertext from being reversed?

  • 1
    $\begingroup$ Where exactly would the key be stored if it is secret? $\endgroup$ – forest Jun 30 '18 at 7:07
  • $\begingroup$ @forest as long as IVs are generated and used correctly, a single 128-bit key could be stored in a HSM. But I'm not sure if the overall solution would be good, so I'm not going to write an answer for now. $\endgroup$ – A. Darwin Jun 30 '18 at 8:09
  • $\begingroup$ @forest a secret key could also be stored in a file (which is blocked from being read from the web server) or as a php environment variable, as long as it is separate from the database it adds an extra layer of security. As Darwin mentioned an HSM could be used however I am not so fortunate to have one on my server. The second part of my question gets at just storing the public key and not the private key, so that the ciphertext couldn't be deciphered. So even if the public key was compromised an attacker would still have to perform the ciphering step. $\endgroup$ – FelisPhasma Jun 30 '18 at 15:56

Q1 : It won't weaken things assuming a few things. Normally storing cipher text is at worst equivalent to storing plaintext. Encryption is invertible. That means no information is lost in the process. It's not different, in that sense, from storing data in a different format. (Raw bytes, base64 encoded text, hexadecimal, etc.)

The assumptions are:

  • The cipher implementation is bug-free. (No guarantee that something doesn't go wrong in some way if encryption can go wrong in some known or unknown ways.)
  • The key is independent of inputs or personal data. (If a key is derived from the password or personal data, then cracking the key means that data gets leaked. Use a randomly generated key.)
  • No side channels. (Implementations of an algorithm can leak plaintext or keys.)


Q2 : That defeats the purpose of encrypting hashes. If you don't have the private key then you cannot rotate keys. This also isn't going to help if people who can crack your passwords have access to the public key.

With the ciphertext, $E_K(H(\text{salt}, \text{password})$, and public key, $K$, the strategy for cracking the encrypted hashed password is about the same as the strategy for cracking hashed passwords.

Normally password cracking is done by generated candidate passwords and testing if $$ H(\text{salt}, \text{password}) = H(\text{salt}, \text{candidate)}$$

Cracking encrypted passwords with known public key K instead tests

$$ E_K(H(\text{salt}, \text{password})) = E_K(H(\text{salt}, \text{candidate)})$$

I assumed the public key encryption method is deterministic. (Or if it includes a IV that value is known, making it deterministic.) If you cannot decrypt the data then you must have determinism because that leaves only comparing ciphertexts as a possible means of verifying passwords.

The intuition behind using public key encryption here is that it means the password cracker cannot decrypt encrypted data and that the encrypted password hashes are therefore no good to him. That's wrong because the public key (if it becomes "public" or at least known to the cracker) enables him to do, on his own computer, whatever the server does for verifying passwords.

Strong asymmetric encryption with a public key after deleting the private key will act like a one way function, just like hashing. You can compute $H(x)$ but not $H^{-1}(x)$. Similarly you can compute $E_K(x)$ with public key $K$ but not $E_k^{-1}(x)$ without $k$, the private key corresponding to the public key $K$. A password cracker does not need the ability to compute the inverse operation. It suffices to use the forward (one-way) function and compare his result to stored data.

(Also, there are scenarios where I wouldn't worry about symmetric ciphers inadvertently having side channels, but I might worry about public key side channels. If, for example, it didn't have a constant time implementation or if attacks requiring physical proximity were a concern.)

Should you encrypt password hashes?

It shouldn't (and probably won't) hurt. It's a plus to be able to rotate keys.

One "helpful" (odd) scenario: Your password database gets stolen, then your encryption key. You find out so you tell your users to change their passwords. The hacker gets your password database again (but not your encryption key for some reason) and wants to crack new passwords. The new password database isn't useful then.

If you're using a typical database it also helps to encrypt data before it goes into the database. If you delete or modify rows, then that doesn't necessarily mean the data disappears from the hard drive. It will disappear from database dumps, but instead of overwriting the space where data used to reside with zeros it might set a "deleted" bit flag or simply change what address a pointer points to. With encrypted data you can do a sort-of-delete of arbitrarily long ciphertext simply by overwriting the key used to encrypt it. It's good to encrypt your hard drive for the same reason.

It's best if you can store your key in a HSM. This means if your password database gets leaked it won't be possible to do offline cracking.

If a hacker can bypass your password check rate limit or is able to execute arbitrary code on your server, however, then he can still do online cracking or use your server for cracking passwords.

Maybe if you have a second server you can implement a kind of poor man's HSM. Use the first server as your front facing web server, allow the public internet to access it. Use your second server to verify passwords on behalf of the first. Do not allow access from the public network to the second server. The second server can store salts and hashes but only return salts in response to a user-id or true/false in response to a hash. It can do rate limiting on verifications against individual user-ids.


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.