How to store encrypted documents for multiple users - php

An online system (front end written in PHP but that's not too relevant) needs to store text in a MySQL database encrypted so that local sysadmins cannot view the data, also so that the data cannot be viewed in the event that the database is stolen. There are multiple users on the system who must have access to the data, they login/authenticate via a standard setup, i.e. username and hashed password in the same db.
As the stored data needs to be decrypted by the system for display to an authenticated user, but local sysadmins must have no way of decrypting the same data, the obvious method would be to have a secret key that is used to encrypt/decrypt the stored data using a symmetrical cipher. The problem (really the question I am asking for advice on) is how/where to store this key?
The authenticated users should not have direct access to the key, so it needs to be stored within the system somehow and used by the software to decrypt the stored files on demand, but also local sysadmins must not be able to learn this key or they could use it to decrypt the stored data.
So one way would be to store the key in the database encrypted, but in order for the system to decrypt and use the key on a per user basis, it would have to be encrypted against something unique to the authenticated user, e.g. their password. Ok, so far so good, but there's a problem...
What if the key needs to be changed? The person changing the key would either have to know everyone's password so they could encrypt the new key against every users account (impractical), or they would have to give the new key to each user and ask them to re-enter it (not an option).
Also, is it good idea, from a security point of view, to have this key effectively stored n times (where n is the number of users) in the database, encrypted with different keys (users password)? i.e. does this expose the key more by giving potential hackers multiple examples of the same encrypted data?
Is there a better way?

Thanks to jonrsharpe for pointing me at Tom Leak's post: https://security.stackexchange.com/a/71915
We will base our system on this method as follows:
Document is stored in a table encrypted using a symmetrical cipher.
The secret key for the above symmetrical cipher is stored in a separate table, once for every user that has access to the document, encrypted using an asymmetrical cipher with the user's public key.
The user's private key is stored in another table encrypted using a symmetrical cipher, the key to which is the user's password.
This means access to the document can be revoked by simply deleting that user's entry in the document key table; If the document is modified the system just needs to delete all the entries for the document in the key table then add them back encrypting with each users public key; additional users can be given access just by adding an entry to the document key table encrypting using the user's public key; user's that have access to the document can decrypt the document's secret key using their private key, which in turn is decrypted using their own password.
Just what we needed!

One solution is to add one more level of a key between the users keys and the DB key. Then if the data is re-encrypted only that one extra key needs to be re-encrypted. It also may be that the need to change the encryption key can be accomplished just by changing this last key rather than re-encrypting the data under a new key.
But perhaps there are other better solutions such as DB roles can be used.
Keep in mind that is just one of the passwords of a user who has access to the encrypted data is compromised the data is compromised and that is escalated with each such user.
Another solution to reduce the number of sysadmins who have access is to put the DB on a dedicated server with only a very few sysadmins with 2-factor authentication and no direct Internet access, only secure access from the server online server.

Related

Encryption between web service users

I've got a web service (similar to an online store) where users save their data. I need to encrypt that data, so that only managers and users themselves could read that data.
Every manager and user has his own password stored hashed in the database.
First i've thought to use RSA(public/private keys), but i can't figure out how to gain access to the encrypted data using different passwords of managers and users.
BTW: i can prompt user and manager to enter his password every time they try to access encrypted data (compare with hashed one in database and decrypt secured data).
Please help and excuse me for my poor English.
Thanks in advance!
EDIT: the simplest way is to use hardcoded master-password for both encryption/decryption, but i think that is very very insecure.
EDIT2: ok, i think i've got the idea:
1. generate unique key
2. using symmetric encryption encrypt data with unique key from (1)
3. encrypt unique key from (1) with user's password and store it with user's credentials
4. encrypt unique key from (1) with manager's password and store it with manager's credentials
now when manager wants to access data, i prompt him for a password, then decrypt hash from (4), get unique key from (1) and decrypt secure data. decryption from user is the same: (3)->(1)->data
so now problem is to make this available for multiple users->user_data/managers, lol
EDIT3: forgot to mention: users create data once and then managers use it for internal use.
If you don't mind prompting for the password every time the user needs to access the data, you might consider using the mcrypt php module. Make sure to do your research before implementing anything, though. It's quite easy to use insecurely if you're not careful.
I believe you should use standard - proven technologies. It sounds me like the way should be SSL connection and well secured server. I believe you definitely do not need to encrypt data, but just provide access only for its owners or by some privilege hierarchy (by webservice security logic). If you need to protect data against disk stole, use encrypted partition by operating system.

In PHP, is it possible to encrypt a string using a key that someone who has full access to the code would not know?

I'm looking to encrypt secure data, such as social security numbers, but there will be other people (e.g. my web host, employees) who have full access to the source code of the encryption. Is it possible to somehow encrypt using a key that does not need to be included in the original source, such that only I (who would know the key) would be able to decrypt?
The way you're thinking about it, no, it can't be done. No, just saying "asymmetric encryption" won't help either. When certain operations are gonna accrue on those data (by a legitimate user) you'd have to decrypt them and wherever your decryption key is stored you're code is gonna try to access is it (meaning that any admin on the server can access it too).
Of course if you want to encrypt the clients' data in a semi-permenant fashion (the users won't be able to alter or read the data again) then yes, the people who suggested asymmetric encryption with you (the developer) keeping the private key secret, are 100% right.
A way of doing it is the following:
User signing up: Generate key pair (private & public) store the public in the database, encrypt the private with the user's password's hash and store it in the database. Now hash the hashed password again and store it in the database.
User entering sensitive data: Take the input and encrypt it with the public key from the database (and as suggested by Hugo, use the user's password as the passphrase for the process) and store it in the database.
User accessing/editing his information: Take the user's password, hash 2 times and authenticate him, then if he's a legitimate user then use the first hash to decrypt the private key and use the private key to decrypt the data (Using the user's password as the passphrase for the process).
Keep in mind that there's no 100% security, take this idea and improve it.
Update: I talked to a friend who works with a payment processing company, he described the real life situation as the following:
There's no way around it, the people running server will always have access to the data, encrypted or not. You have to keep the private key some where. We keep SSNs and Credit Card Numbers in a separate data database on a separate server that has physical security and only authorized people are allowed to access that server. We don't query the secure database except using scripts on the same server and those scripts provide us with bare-minimum API that will handle all the payments. In our plain-text database we keep only a portion of the information (XXXX-XXXX-XXXX-4569) for viewing purposes only. All editing, reading, appending, adding, removing happens on that secure server (secure software, locked doors, security cameras) through the API.
The short answer is no.
You could, however, use assymetric keys, such as GPG keys (or SSL keys).
You have a private and public key. The public key is used for encrypting, and the private key for decrypting. You could include the public key in the source code, and only keep the private key to yourself.
you could use mcrypt
example encoding:
$key = "mykey";
$data = "my data";
$enc_data = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $data, MCRYPT_MODE_ECB, md5($key));
example decoding:
$data = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $enc_data, MCRYPT_MODE_ECB);
the $key can be given outside of source code or remembered and given through an html form.

How to store the encryption keys securely in php code file

I have a website that users submit sensitive data to it then a php script encrypts these sensitive data using rijndael 256 and store it in mysql database
the problem is that I want to store the key in a secure place that can be accessed only by the php script and not to be seen by any other one
Depends on how high a security you need for the data. You could consider having a different security key for each user, by possibly encrypting the data that identifies that particular customer and attaching it onto the 256-bit encrypted key. But better still would be split the key up and insert that encrypted data throughout the key. Makes it more complex to decipher. This would mean if a programmer has access to the key the programmer can't simply decrypt everyones data without having access to the customer encrypted data as well which would be different for every user.
And yes it is true that the programmer can still echo the key out to the screen but they would ALSO need the customer encrypted data for each customer they want to decrypt the data of.
You could also consider Public and Private Key Encryption instead if applicable. The customer/user could generate their own keys. Customer places the public key into a form on the website which gets stored in the database, then the customer/user would have the private key to decrypt the data. You use the public key to encrypt the data. This would mean each user/customer would have their own set of keys. The Private key could possibly be even placed on a keycard and hooked to the computer to verify access.
More information # http://en.wikipedia.org/wiki/Public-key_cryptography
One alternative would be to have the PHP script call an external script (doesn't necessarily have to be another PHP script; it could be anything) that would have access to the key. As long as no one has write access to the external script, or read access to it if you hard-code the key into it, then it should be relatively secure. If you store the key in a separate file, that file needs to be readable/writable only by the owner of the external script.
You could encrypt/decrypt with certificates and have the server request a password for the certificate upon bootup.
The good thing is that your key is only in memory and can be different for every installation/server.
However, this method is quite a pain and generally only works when you have your own system administrators/are not dependent on a third-party hosting provider.
The intermediate solution to generate an encryption key per section/user/client in your database and encrypt the sensitive data with this per client key. These per-client keys are encrypted with a master key and stored in your database, while the master-key is stored somewhere in on disk with minimal priviliges.
This will not safe you when your server gets fully compromised, but does limit the risk in case of -for example- a data-leak/partial compromise.
If the problem is you don't trust a) the programmer or b) the system administrator, you are out of luck.

Two-key encryption/decryption?

I'm looking to store some fairly sensitive data using PHP and MySQL and will be using some form of reversible encryption to do so since I need to get the data back out in plain text for it to be of any use.
I'll be deriving the encryption key from the users' username/password combination but I'm stumped for what to do in the (inevitable) event of a password being forgotten. I realise that the purpose of encryption is that it can only be undone using the correct key but this must have been addressed before..
I'm trying to get my head around whether or not public key cryptography would apply to the problem but all I can think of is that the private key will still need to be correct to decrypt the data..
Any ideas?
It's not clear what you are striving for, so advice on how to implement it is hard.
Standards like PGP and S/MIME encrypt each message with a new symmetric key. Those keys are then encrypted for each recipient of the message. This way, instead of duplicating the message (which could be very large) for each recipient, everyone gets the same ciphertext, and only the key (which is small) is duplicated—but encrypted differently for each recipient.
Maybe you could do something similar here, encrypting the key with the user's password, and encrypting another copy with your public key. If the user forgets their password, you can recover the message for them (after an appropriate backup identity verification) using your private key.
The conventional solution is to have a "recovery agent": one user that holds a second password that can be used to decrypt all data. Strict usage policies would apply to using the recovery password, such as putting it physically into a safe.
Then, either encrypt all data twice: once with the user key and once with the recovery key; alternatively, generate a session key for every set of data, and encrypt the data only once, but the session key twice.
For that to work, at least the key of the recovery agent must be asymmetric, since the private part will live in the safe, and the public key in the software.
As yet another alternative using the same scheme: encrypt the user's passwords with the recovery key on password change. This is simpler to implement, but will allow to recover the passwords and not just the data, which may be undesirable.
I'm looking to store some fairly
sensitive data using PHP and MySQL and
will be using some form of reversible
encryption to do so since I need to
get the data back out in plain text
for it to be of any use.
Protecting sensitive data is good. Now:
Whose data is it? (yours, your user's, or a third party?)
What does it need to be protected from? (disclosure, corruption (accidental or intentional...)
Who does it need to be protected from
Uninvolved parties goes without saying.
Do you need / want to avoid accessing the plaintext data yourself (useful for deniability),
Do you need to protect either your user's data from being visible to a third party,
Or a third party's data from the user,
Or your data from the user or a third party?
What are likely attacks?
Do you need to protect in the case where the server is completely compromised?
Do you need to protect against an application level attack where the user simply gains access to some but not all available data (e.g. access to the SQL database, but not the filesystem)?
Will the amount of data be small enough that the attacker can guess and simply check whether he/she got it right? (short passwords, numbers, simple words, fixed form text are likely candidates)
Will the attacker have known plaintext with which to attack?
Is it better for the data to go away (or to re-retrieve the data) if the user forgets their password, or is it worth an increased risk of exposing the data to avoid that cost?
There are probably other questions, but this is the type of thing you want to think about when using encryption. The answers will help you figure out what you need vs. what you want, and will probably help point in the right direction. You might not want to share all of the answers with us.
I'll be deriving the encryption key
from the users' username/password
combination but I'm stumped for what
to do in the (inevitable) event of a
password being forgotten. I realise
that the purpose of encryption is that
it can only be undone using the
correct key but this must have been
addressed before..
You might have decided on a solution without considering the impact. That doesn't mean the solution is wrong, but this question suggests you should think about what you are willing to risk for security. Sometimes data will be risked.
I'm trying to get my head around
whether or not public key cryptography
would apply to the problem but all I
can think of is that the private key
will still need to be correct to
decrypt the data..
This too sounds like a solution in search of a problem. Public key cryptography is useful when you have two (or more) separate actors with an interest in communicating data between them. Those actors can be real (people) or functional (components of a system), but without two actors, there is no reason to have a separate public and private key.
Basically, if you encrypt something, and lose the encryption key, you're screwed.
When it comes to securing data, you need to consider why you're securing it, and what you're attempting to secure it against. And what tradeoffs are worth making in order to do so - the only truly secure system is one that is completely isolated from the internet, which is a level of security that is self-defeating for most applications.
So here are some questions to ask yourself:
If someone compromises my database, is it acceptable for them to be able to access this data?
What if someone compromises my entire application stack?
If the answers to the above two questions are "no", then the key material must be held by the user. And they will lose access to their data if they lose the key.
You can provide an option for manual key recovery if you also have a "master key" that you don't store anywhere near your application, only you hold it and you use it to manually reset passwords. If that's also not an option (say, only the user should be able to access the data, not the system administrator), then you're going to have to make a compromise somewhere.
This is a question I have thought about myself and as I see it the following options are available (with option #1 being the most secure):
Provide no reset password functionality - if they have forgotten their password then they are locked out.
Generate a new secure master key and encrypt & hash the user's key with this master key and store the cipher text and hash result in the database. The secure key is then made known to the user either by adding it to a file that the user downloads, emailing to the user or displaying the secure master key on screen. To reset the password the user would have to enter this master key which is then hashed and compared and if they match, the user's key in the database is decrypted.
Ask the user to provide 2 security questions and answers when registering; hash the answers and store the questions and answer hash in the database. The second answer is used as the master key to encrypt the user's key. To receive a password reset request email the user has to answer the first question correctly. Once they click the link in the email the web page then asks the second question, if this is correct and the query string parameter values are valid then use the answer to the second question to decrypt the user's key.
Use an application global master key (maybe stored in the web/UI application and use this to encrypt and store the user's key. Once a user is verified through a password reset email process the user's key is decrypted using the application global master key and then reencrypted with their new password.
In summary, the benefits of each option is as follows:
This is the ultimate for security and would possibly be the only option if the data was critical to be kept encrypted. However, in the real world people forget their passwords as sure as the sun rises and not providing a reset password function could be a bad commercial decision.
This is secure as the master key is not stored on the front end or database so if the platform is compromised then the data would require some significant effort to decrypt. However, the downside is the user could still lose their master key anyway.
The weakness here is if the database is compromised the answer to the question could be researched and then used to decrypt the users encrypted key.
This approach leaves the application key in the stack leaving your data vulnerable if your platform is hacked. The only protection you have is that if the database server is hacked then the data would still be safe.
As with most things in the world of software development you need to consider what is best for what you are trying to accomplish and aim for the correct balance.
Why are you using a different key for every user?
If you choose one key, it is much easier to handle.
Store your encryption key outside of the database.
Your application will still have to have access to it, but someone with a db dump will not be able to read the encrypted info.
Generate a random session key.
Use the session key to encrypt the data.
Encrypt the random key with any number of user passwords that you need.
This way you can use any user password to decrypt the data.

Two way DB encryption secure even from the Admin

I have an interesting encryption problem at hand. I do not know if it can be solved but here goes:
A database is to contain sensitive user information. As such, the user information must be encrypted (two way encryption). The user has a login / password and these may be used in the two way encryption. Now, the encryption is to be designed in such a way that even the administrator viewing the database tables should not be able to make sense of the user information.
However, the design has to take care of the cases where the user may forget her password. If the password is used for encryption, forgetting the password means the information is lost - not wanted. If the user password itself is stored in the database using a two way algorithm (instead of a one way hash) with a key hardcoded in the script, the administrator can discover the hardcoded key by looking at the script (the administrator of course has access to the script).
Does anybody know a way out of this?
PS: This is a real problem. My company is an absolute security fanatic (ISO 27001 and all) and I have been entrusted to design a system with the above mentioned functionality. By the way, I am using a PHP script and MySQL.
EDIT: Perhaps it was not clear earlier, the user needs to see / edit this user information on a day-to-day basis.
What you want is a recovery agent. Encrypt all data twice: once with the user key, once with the recovery agent (public) key; atleast the latter one needs to be asymmetric. Keep the recovery agent key in a pyhsical safe, with a formal access protocol (e.g. four eyes principle). Usually, the administrator cannot access the encrypted data, but if the user loses the key, and recovery is authorized, then the recovery key is obtained.
There are also ways to encrypt the recovery agent's key so that m-out-of-n people have to agree to use it.
Edit: One implementation strategy is to encrypt everything twice. Alternatively, for each data set that needs to be recoverable independently, create a fresh symmetric key, and encrypt only that key twice; the original data get encrypted only with the session key. That approach can extend to multiple independent readers; it requires asymmetric keys per reader (so that you can encrypt the session key with the public keys of all readers - one being the recovery agent).
I copied the terminology from Microsoft's Encrypting File System, which has that scheme implemented.
Can't be done.
In all cases, someone has to be able to recreate the key to decrypt it. Let's consider the options:
Key stored on server. Fails: administrator has access.
Key encrypted with user's password. Fails: user might forget it.
The solution is to relax the administrator having access restriction, and instead of impossible, you make it just very difficult. For example, if the data were encrypted with a key stored encrypted with the user's password, but that key were escrowed in some other system which can't be accessed in the normal course of events by the administrator (perhaps only another admin has access?) then you can still recover from a user forgetting their password (with intervention of whoever has access to escrowed keys), but the admin can't just download your database and read all the data.

Categories