We have a web app, which is written in Java, and storing data into a PostgreSQL database.

We'd like to encrypt a few fields in our database, as well as some uploaded documents. However, these all need to be 2-way encryption (ie, we need to be able to decrypt them), and decryption needs to be fairly fast.

However, we cannot come up with a "secure" method to actually encrypt/decrypt the data. Because this is a web-app, and there is no client, all of the encryption keys are going to be stored either on the web server (in plaintext, or our actual code), or the database server.

Any other ideas on how to actually make this at least moderately ... secure?

4 Answers
4

No, there isn't. If your business layer must access the raw (unencrypted) data, then anyone who can hack your business layer (i.e. peek inside some keyword stored inside your application code or a file readable by your application) can also access the data. See also this related question.

Implementing some encription with the decoding key readable by the application only gives you a slight protection from casual data spying, and from some user roles or cases (example: a DBA who can read the DB but not the webapp; or someone who stole a dump of the DB, etc). But that's all.

see my answer for an example of how yes, there is a way.
–
BorealidJul 14 '10 at 21:33

@Borealid: yes, there are ways... impractical for most scenarios. A "password provided manually each time the application starts", for example, is hardly practical for a web app.
–
leonbloyJul 15 '10 at 1:27

If you leave the password in the application source (or configuration files), then an attacker would still need to physically obtain the token to compromise you. There's always a tradeoff between security and usability.
–
BorealidJul 15 '10 at 2:01

If you use a smart token with a PKCS11 interface, generate a keypair on the token, and then secure access to the keypair with a password provided manually each time the application starts, then you can at any time in the future remove the ability to read the data by removing the token. In the event that fault tolerance is necessary, dual-encrypt using a pair of tokens.

The important principle here is that the private key is usable by the application, but not readable by the application. So, looking at the application source gets you nothing.

This solution will not stop someone who accesses your application while it is running from reading your data. But you couldn't stop that anyhow - they could read your data from memory before encryption.

This will, if correctly implemented, securely protect you against all forms of offline attack. The hacker cannot physically break into a good hardware token. The key cannot be extracted without knowing the password. The password cannot be guessed because the token has hardware limits on brute-force attacks. Thus, the only way to recover the data is to either steal the token and the password, or brute-force the data encryption key (which could be a 2048-bit RSA certificate).

AES is a well-established standard for symmetric encryption and support for it is built into the JDK, so I recommend you use that.

Now, you are quite correct that the key needs to be stored in such a way that your application can access it. However, rather than storing the key in your code or in a file, you can "split the secret up" into different physical locations to make it harder for an insider to beat your encryption. For example:

At development time, generate a new
key and "hard-code" it into your
program. Call this Key A.

At installation time, generate a
second key. Call this Key B. Now
encrypt Key B with Key A and store
the encrypted version of it in the
file system. Make the file readable
only to the user under which the
server runs.

For each value you encrypt (say,
each credit card number), generate a
third key. Call this Key C. Use Key
C to encrypt the value. Then encrypt
Key C with Key B and store the
encrypted version of it inside the
database. (You could use a separate
field in the same table.)

To decrypt, you would read the encrypted Key C from the database and the encrypted Key B from the file system. Use Key A to decrypt Key B, and Key B to decrypt Key C. Then use Key C to decrypt the value.

What does all this buy you? The idea is to spread parts of the secret around to several different locations, ideally controlled by different groups of people. Anyone who wants to crack an encrypted value will need access to all three items: the program code, the file, and the database. You could take this a step further and add more keys to the chain, but you get the idea.

@Rob H: This is security by obscurity, since compromising the application compromises key C and bypasses the entire chain.
–
BorealidJul 14 '10 at 21:37

Not sure what you mean by "compromising the application". If you mean viewing the source code, then that's not true because you can't decrypt Key C with just the source code.
–
Rob HJul 14 '10 at 21:46

@Rob H: Controlling the application or accessing its memory, via code injection, root access to the server on which it is running, cold-boot attacks, side-channel disclosures, etc etc. Key C is decrypted while the application is running, and so is vulnerable.
–
BorealidJul 14 '10 at 21:59

Well, certainly, but most of those threats exist with any approach including the one you propose in your own answer. ;-) My approach defends against a rogue developer or DBA who doesn't have access to the running web server, but might have access to the source code or database (or both). As for cold boot attacks, you could place Key B in an encrypted file system that's manually unlocked on boot or use a hardware token. But clearly, nothing offers complete protection from someone with full access to the running server and database.
–
Rob HJul 14 '10 at 22:42

@Rob H: The difference is that, in the solution I propose, being able to passively read memory does not compromise the whole database. Instead, it only compromises the keys which are actively decrypted while the attacker is watching. That's the difference between having to tell a dozen users who logged in at the wrong time they were compromised and having to reissue every password in your whole system.
–
BorealidJul 15 '10 at 0:16

Pretty old question, but for the record I'm describing what I figured out as my own solution, which looks secured for me.

During the admin user creates its account, I generate random encryption key. This key is then encrypted in AES using admin password and stored in database. The admin password is also secured (by hashing or whatever spring password encoder). On this stage no one can access the real encryption key.

When the admin logs in, he gives the plain password, which is compared with the encrypted user password. If it's correct, it's used to decrypt the encryption password, and forgotten. The encryption password is then held in the session. This is the only vulnerability of this mechanism - if someone hacks into the server and makes the memory dump, can access the passwords, but only for currently logged users, and never having only the application or database. I'm currently thinking about adding another level of security to this (eg. encrypting passwords in the memory by eg. user log-in time). But, anyway, even if you require from your user to enter password on each request, you cannot guarantee when it's kicked out from the memory by GC and you need to have it in the memory at some point.

So, the user session knows the encryption password, which is then used for encryption and decryption. When admin creates new users, he has access to unencrypted password during this - when he enters the new password for new user, there's another copy of enrypted password created and kept in db, encrypted by this user password, etc.

Do you see any other vulnerabilities of this mechanism, besides that someone hacks the running server and dumps the memory?