8

I've made a "vault" web app in PHP for storing passwords, credit card numbers, etc. It's mostly just for myself, but I'm practicing building it with multiple users in mind. I use two way encryption to secure the data, and want the user to login using their key (vs both a password to login AND a key for their data.)

I initially stored the username in plaintext, and the key/password one way encrypted with a salt. My first concern is that if someone had access to the SQL data, they'd probably have access to my code (my salt and encryption method) making shorter work of revealing the password, then being able to access the two way encrypted data with it.

I also didn't like the username sitting there in plaintext...that's half the login info right there.

Basically, I'm worried about the login giving away extra info about the encrypted data itself.

What is the best practice for logging in, and two way encrypting data with the same password/key?

...and does anyone know where can I learn more about this specific concept?

Joel Mellon
  • 191
  • 1
  • 5

3 Answers3

16

First of all, don't worry about the username. The username is supposed to be public information — if it wasn't, it wouldn't be a username, it would be a password. Just assume that an attacker will be able to find out the username(s) in any case, and design your system to be secure with that assumption.

Second, don't worry about the encryption (or hashing etc.) method being public, either. That's Kerckhoff's principle — the encryption method should be treated as public, the only thing that needs to be secret should be the key (or password, in this case).

There are many good reasons for this, not the least being that it allows you to publish how the system works (and thus convince your users that it's secure, or to ask for help from security experts, or to just use an existing standard system designed and analyzed by such experts) without compromising its security.

Third, don't worry about the salt either. The salt should also be treated as (potentially) public knowledge. Its purpose is not to provide any additional secrecy, but to protect against attacks using precompiled hash tables by ensuring that every user's password is hashed in a different way, even if the actual passwords happen to be the same.


OK, so what should you do, then?

Well, first of all, you should use a secure key derivation function that implements key stretching (and salting, of course) to derive the user's encryption key from their password. PBKDF2 should do nicely for that, although e.g. scrypt might be even better if you have it available.

Second, you obviously should not use the encryption key as the password hash. In fact, you shouldn't store anything in your database that could be used to obtain the encryption key without knowing the password. (For instance, the encryption key should not be a hash of the password hash.)

One thing you could do is derive both the encryption key and the password hash separately from the password using PBKDF2, but that would be kind of inefficient, since PBKDF2 is deliberately slow. Instead, you could derive the encryption key from the password with PBKDF2, and then use a normal cryptographic hash (e.g. SHA-256) of the key as the password hash for logging in. (Or, if you want to be extra careful, derive both the encryption key and the password hash from an intermediate key derived from the password using PBKDF2.)

Finally, to make changing the password easy, don't directly encrypt files with the user's encryption key. Instead, for each file, create a random file key, encrypt the file with the file key and encrypt the file key with the user's encryption key. That way, when the user wants to change their password, you only need to re-encrypt the file keys.

Ilmari Karonen
  • 4,386
  • 18
  • 28
  • You've addressed the primary concepts behind my concerns. So many big words to learn! Thanks, I'll be reading up on this. –  Oct 30 '12 at 22:49
  • 1
    Can you clarify your, "Second, you obviously should not use the encryption key as the password hash."? Do you mean I should not use the password **hash** to encrypt my data? –  Oct 30 '12 at 23:39
  • Same difference. The point is, you need to derive two values from the password: an encryption key to en/decrypt the data, and an authentication key that you save in your user database and compare against the derive value to see if the password is correct. Both of these should be derived from the password using PBKDF2 (or equivalent), but they should not be the same -- otherwise anyone with access to your user database would be able to decrypt every user's files. For the same reason, deriving the encryption key from the authentication key would be a bad idea, but the other way should be OK. – Ilmari Karonen Oct 31 '12 at 00:22
  • OK, so my initial setup was the right concept but I learned that using a KDF is much more secure. Your unique key per file idea is really awesome, but I'm not encrypting files, just a single column in a db, so changing passwords would be about the same in effect. Overall I learned a ton and I now feel confident in this approach. Thanks very much! – Joel Mellon Oct 31 '12 at 15:50
  • But with this one we won't be able to setup forgot password feature. Is there a way to do that? – Pritam Banerjee Dec 31 '18 at 02:00
  • @PritamBanerjee: One possibility would be to let the user download a "key recovery file" that contains their master file encryption key, which they can use after a password reset to regain access to their old files. (Better yet, use a [secret sharing scheme](https://en.wikipedia.org/wiki/Secret_sharing) to split the master file encryption key into two shares, and let the user download one while the other is kept on the server. That way, if the downloaded share might be compromised, the user can still render it useless by logging in and telling the server to erase its corresponding share.) – Ilmari Karonen Dec 31 '18 at 12:29
  • @IlmariKaronen Assuming data is encrypted using with a symmetric algorithm like AES and the key is derived with PBKDF2 from the user's password and a salt, and the data always has a certain structure, for instance it is a valid XML document, is it in your opinion valid to parse the decrypted data, and if parsing succeeds, consider the password to be 'correct'? – Alex Suzuki Oct 16 '19 at 07:36
  • @AlexSuzuki: I would not recommend that. It's inefficient, potentially exposes your XML parsing to attacks, and the probability of detecting an incorrect password is hard to quantify. I would instead recommend doing what I've described e.g. in [this answer on crypto.SE](https://crypto.stackexchange.com/a/9619): derive a "master key" from the password using PBKDF2 (or scrypt / Argon2 / etc.) and then derive both the file encryption key(s) and a "password check value" from the master key using a fast KDF such as HKDF-Expand. Oh, and use an authenticated encryption scheme like SIV or GCM-SIV. – Ilmari Karonen Oct 16 '19 at 08:13
  • Hi @IlmariKaronen I completely agree on the inefficiency and the potential of opening up an exploit window through the subsequent parsing step. But apart from that, is it inherently insecure? The decryption and parsing in this case is running on a client machine, if a wrong password generates a structurally valid document (highly unlikely but hard to quantify as you mention), that would be acceptable because a subsequent authentication at a server using the decrypted credentials from that document would then fail. – Alex Suzuki Oct 16 '19 at 08:39
3

Eric Lippert's blog series "You want Salt With That?" was helpful for me in understanding several issues about security. It was written in 2005 but the information still holds up today.

You could also check out OWASP's Guide to Authentication and their Authentication Cheat Sheet

Those documents should prove helpful but in regards to What is the best practice for logging in... I don't think there is an easy straight forward answer on that. I am also not a security expert, hopefully someone who knows more is willing to step in and give you advice on that.

  • "You want Salt With That?" was a pretty good read. It reinforced some of my concerns about my system. ie, which parts can be leaked, and which ones are useful to an attacker. Manly, reminding me that I need the max depth of defense, because I know enough about security to know the data should be secure even if the attacker knows everything there is to know about the algorithm. –  Oct 30 '12 at 22:48
  • It also reminded me that salts should be unique. I already know this about IV's but forgot the obvious here. –  Oct 30 '12 at 22:51
  • 1
    @sudopeople A static salt can be used in conjunction with a dynamic salt, but it is known as [Pepper](http://security.stackexchange.com/questions/3272/password-hashing-add-salt-pepper-or-is-salt-enough) – Scott Chamberlain Oct 31 '12 at 20:35
0

I use the user's password as the basis for the encryption key, and use this to encrypt a json-encoded string of the user's permissions and store this along side their user name.

On login, the user name is the only field which is searched for in the database.

When a matching row is returned, the encrypted data comes out of the database its then decrypted with the password. If this is valid json, its decoded and used - else the password was incorrect

EDIT

Additionally I could add some random data the json, like a salt, and re-encrypt and update in the db to add some time-variance to it

  • This is dangerous as encryption modes like CBC are malleable to a certain extent. It would be much better to use authenticated encryption like AES-GCM. – AndrolGenhald Feb 05 '18 at 22:56