A common scenario in web applications involve using a single password as a means of authentication as well as a means to derive a secret for use in encrypting data.
Many strong key derivation functions like pbkdf2, bcrypt or scrypt have
properties that make them strong password hashing functions as well. However,
the same derived value cannot be used as an encryption key and a password hash.
The password hash value has to be stored by the server to compare against the
provided password in future authentication attempts. If this same value is
used as an encryption key, an attacker that compromises the server will be
able to decrypt the data easily.
There is an easy solution for this problem. While I will be using pbkdf2 as
an example, pbkdf2 can be substituted for any strong algorithm like bcrypt
or scrypt.
-
Generate a random key
kusing a cryptographically secure random number generator. This means usingCryptGenRandomon Windows and/dev/urandomon *nix operating systems. This random keykwill be used for encryption. -
Generate two salts
s1ands2and store them in plaintext. -
Compute
pbkdf2(password, s1)and store this value. This will be the password hash you use to compare against for future authentication attempts. -
Compute
pbkdf2(password, s2) xor kand store this value. -
When the random key
kis required for encrypting or decrypting data, simplyxorthe value ofpbkdf2(password, s2)against the value computed in step 4.
The advantage of this scheme is that the encryption key k is not tied to the
password. This means that passwords can be changed without re-encrypting
the data with a new key repeating steps 1 - 4. A very useful property to have
in the event of a server compromise where passwords have to be reset en masse.