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 da开发者_开发百科tabase, 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?
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.
You actually can make this a bit more secure.
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.
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?
精彩评论