I am trying to figure out a good way to store a user's encrypted data in such a way that only the user can access it.

I have a master key located outside the web root, called $serverKey. And when a user log's in, I put another key client side with sessionStorage.setItem(). the $clientKey is set by sha256 of their plain-text password (which of course isn't stored anywhere). When the user submits data, I use openssl_encrypt() with $serverKey.$clientKey and store that in my database.

So my goal is that in order to hack the user's data you have to somehow get the master key and the client's key. What are the holes in this plan? I realize that changing and forgetting passwords causes problems, but I'm ignoring that right now.

  • What about changing passwords? You could say what is the data, its lifetime and what kind of database you are using?
    – Aria
    Aug 25, 2016 at 21:41
  • Well, I'm just starting to learn about encryption, so I'm trying to speak generally. If the user wants to change passwords, then I could re-encrypt everything. Suppose the data is posts in a private forum. MySQL.
    – twharmon
    Aug 25, 2016 at 21:47
  • And how about recovering password if forgotten?
    – Aria
    Aug 26, 2016 at 13:41
  • Is it possible to have 100% end to end, if all the keys are available outside the user? I thought 100% means that one of the keys has to be the password, which only the user has. If the data can be accessed without the pasword, can it be considered true end to end?
    – twharmon
    Aug 27, 2016 at 16:46
  • 1
    It's not an answer to the question, but the singular best advice is: Don't invent your own crypto. You say yourself that you are "just starting to learn about encryption", and there are many, many, many nuances that you have to get right for a cryptosystem to be both viable, workable, and actually protect against threats beyond that of your little sister. Unless this is a downright toy project, and you can guarantee that it will always remain a toy project, use tried and true cryptosystems (not just primitives, but actual cryptosystems) designed by people who know what they are doing.
    – user
    Sep 25, 2016 at 19:32

2 Answers 2


in such a way that only the user can access it.

It makes no sense to encrypt the data the server have to operate (unless you are exploiting malleability). Fully homomorphic cryptosystems are infeasible now and not so well studied, so it is infeasible to protect user's data from a privileged party.

So let's suppose you try to mitigate offline attack, when an adversary obtains a dump of server db with user's data in it and tries to decrypt it without knowledge of user's passwords. The adversary is unable to modify the code running on the server and unable to monitor the data the server gets/gives from/to another users.

You use SHA-256 of the data having poor entropy, which means it is easy to make a dictionary brute-force. So my first advice is to replace SHA-256 with some KDF (hash functions intentionally made resistant to brute-force at cost of running time and memory), for example the Halting KDF, and have personal salt for every user.

Changing passwords is not a problem because you are able to re-encrypt the data.

  • To protect against poor entropy a salt will only help to a certain amount. You'd need a PBKDF such as PBKDF2 or bcrypt instead of a KBKDF such as HKDF. Dec 24, 2016 at 13:18
  • Sorry, but I meant Halting KDF under hkdf unaware that there exists another meaning.
    Dec 24, 2016 at 22:26
  • Edited this in if you don't mind. HKDF is used in e.g. TLS and I doubt anybody will directly link HKDF with the Halting KDF in the future (except possibly the author of that paper, of course). Dec 24, 2016 at 22:51

Let's split this in two:

Encrypting the user's data with their password

Sure, why not, if the data is private. In practice, the server will need to handle it in plaintext, but the data can be left encrypted as long as the user is not logged in. Though in a message forum, messages that should be readable by other users can't be subject to this treatment, so it pretty much only works for private messages (addressed to a single user or a known group of users).

As always, you don't want to use any password directly as a key, but run it through some key-derivation function like PBKDF2. This will slow down brute-force searching the password, and give the possibility to generate longer keys than the password is (in case that's required).

As for password changes, the usual practise is to generate a random strong key, used to encrypt the data, and then store this key encrypted with (a key based on) the user's password. This way changing the password only requires re-encrypting the master key, not all the data.

Though the downside here always is that if the server is compromised, it may be configured to save the user's password in plaintext when they next log in, or just save all the data unencrypted.

Encrypting all data with the server's key

Here, I wonder what the expected threat is? A software attack that leaks all the (encrypted) data, or the server hardware getting lost? Protecting against misplaced hardware would require encrypting the data with a key that is not on the system itself, but perhaps entered by hand. The easiest way to do this would be to use some full-disk encryption solution.

As for an attack that somehow leaks all the data, but doesn't allow the code itself to be modified, sure, encryption with a fixed server key should help. But do you need to, if the data is already encrypted with the user's key? How feasible is it that the encrypted data would leak but not the server's key, given that the server is going to need that key for almost every operation involving the data? I have to say I don't know.

(If you're thinking SQL injections, better fix your practices on that where the problem is. On the other hand if you're thinking of a vulnerability that leads to arbitrary code execution, then the attacker just got the server's key, too.)

Combining the two

The obvious way to combine two keys would be to encrypt the data twice, using both keys, such that the stored data is C = E(K2, (E(K1, P)). This is what would happen in the end if you store encrypted data on an encrypted drive, but it's a bit of a waste or work.

Another way would be to derive the actual encryption key from the two keys (server's and user's) using a KDF (again). Though with random-looking inputs, I think you might get away with combining two fixed-length keys with a strong hash like SHA-256, so C = E(H(K1, K2), P).

  • I don't see how encrypting the user's data with their password would work in case of private messages. The sender presumably doesn't have the recipient's password, so it is impossible to encrypt the message using the recipient's password as a part of the key; the recipient presumably doesn't have the sender's password, so it is impossible for them to decrypt the message if the sender's password is used as a part of the key. Either way, you lose.
    – user
    Sep 25, 2016 at 19:29

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .