1
votes

I'm building a Django website with MySQL. I've already decided to use Django's built in pbkdf2-sha256 with randomly generated salt hash to store a user's password.

However this website will also need to store third party login credentials for many other websites (who don't use oauth). So I was looking into AES-256 encryption and of course the issue becomes where to securely store the encryption key.

Now here's my solution: Let each encryption key = the hash of the users actual password and a randomly generated salt (different from the salt already used for the password stored hash). The salt would be stored in the table, the actual password and the hash of it obviously not. So the encryption key would be generated on login and stored temporarily but expire on logout. Further someone compromising the server can't generate the encryption key without cracking the original pbkdf2-sha256 hash and even then, it would only be for that one user, not a universal key.

The downside is that if they change/reset password, they would have to re-enter their credentials for each site. But that's not a huge deal and seems to be way more secure than storing the key somewhere on the server or even a different server.

But I only learned what a hash is 24 hours ago so what do I know. Am I overlooking something or is this reasonably secure? Or is there a better way?

1
That's what PBKDF is: Password-Based Key Derivation Function.Remus Rusanu
I realize that. And that's why I use it to create a 1-way key instead of the users passwords. I'm suggesting using the password again with a different salt, to use as the individual encryption key to different login credentials. That way no attacker has access to the encryption keyRyan F
You generate high entropy salt and use the PBKDF on the salt+password to obtain the key+IV to encrypt the data and store the salt and the encrypted data and this is long term storage. When needed, you use the stored salt and the password (retrieved from where?) to access the encrypted data. But you don't have the password. You only store a one-way hash in your user info. And the login form post is long gone. Unless you ask user for pwd every time you need it, you need to store it somewhere, and that is your real problem, where do you store the PWD?Remus Rusanu

1 Answers

3
votes

The algorithm PBKDF2 which you mention is actually designed for this explicit purpose.

So, the workflow would be to generate a random salt. Then store that in the database for the user.

Using PBKDF2 with a high iteration count and the salt, generate 640 bits of key material (80 bytes).

The first 128 bits become the IV for the cipher

The next 256 bits become the cipher key (the key used for AES-256)

The final 256 bits become the MAC key (the key used to authenticate the encryption).

key = PBKDF2-SHA256(password, salt, 50000, 80)
iv = key[0:128]
cipherKey = key[128:384]
macKey = key[384:640]

Then, encrypt the data using those keys (pseudo-code):

ciphertext = AES-256-CBC(data, cipherKey, iv)
authtext = SHA256-HMAC(ciphertext, macKey)
result = '{}{}'.format(authtext, ciphertext)

Now, on decryption, just walk back in reverse...

key = PBKDF2-SHA256(password, salt, 50000, 80)
iv = key[0:128]
cipherKey = key[128:384]
macKey = key[384:640]

authtext = result[0:32]
ciphertext = result[32:]

if !timingSafeComparison(authtext, SHA256-HMAC(ciphertext, macKey)):
    return false

return AES-256-CBC-DECRYPT(ciphertext, cipherKey, iv)

Yes, if your user forgets the password, all encrypted data is gone. But that's what you want, right?