How to store the encryption keys securely in php code file - php

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.

Related

How to store encrypted documents for multiple users

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.

Where should I store an encryption key for php?

I'm writing a php application that accepts sensitive customer data, and so I need to encrypt it before storing it in a mysql database. I'm going to use mysql's built-in AES functionality to do column-level encryption.
I want to avoid storing the encryption key on the server, and so i'm going to provide a web-page for an administrator to log-in, and enter the encryption key. I want to store this key in memory while the application is running, but never permanently to disk.
What is the best way to do this?
Can I modify the $_SERVER array to store information between requests? Can I store the key with apache in some way? Maybe shared memory?
Rather than rely on MySQL AES for encryption, why not use PHP's native openssl encryption scheme (a PECL extension). This requires a private and public key, public to encrypt, private to decrypt, and the keys can be kept in separate places.
I ended up storing the encryption key in an in-memory table. All access to the database is done through a set of stored procedures; The stored procedures include the ability to do key management (i.e. insert key to memory-table, change keys, check if a key has been entered etc.), as well as store/retrieve application data.
With this design, the database credentials left on the application server only have the authorization to query through the set of defined procedures, and have no way to read the encryption key directly.
I liked this approach because:
The key isn't stored on the file system - preventing issues with hardware disposal at end-of-life, and from prying eyes of system administrators
The application code can't get access to the key (other than while entering it), so the only place it will ever reside is within the database process.
All logic for encryption/decryption is embedded within the SQL queries, so don't need to worry about application code doing it correctly - Nice for maintenance.
One possibility is to create a RAM disk and store the key there.
The safest place to store any kind of encryption key is on the server NOT in the database, and make sure it is owned by root and not readable by others.
Write a php config file and store it in your home directory. Allow only php to have access to it.
$cryptKey = "aac1ebadcfabdef72376acd" ;
Include at the top of every php page that uses the encryption key using an absolute path to the home folder. This folder is not accessible to the end user.

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.

PHP, MySQL, and AES Encryption / Decryption for User Data

I am new to AES encryption but trying to build a solution which:
Accepts consumer data
Encrypts that data using AES and a
"public" key
Store that data in a MySQL database
Have the ability to pull and decrypt
the data ONLY with a private key
(stored on my personal machine, not
the server itself).
I realize this may be overkill but want to be overly protection for my consumer data.
A few things to note:
This is not credit card information
so please don't write telling me
about PCI-DSS, it is other form of
personal information all under 500
characters in length for each field.
I may store pieces of the consumer
information and others in a second
database tied together by a unique
member ID for additional security.
Incoming MySQL calls can only be
made to my server directly from my
static IP.
SSH root is disabled, ports changed,
and so on so I feel my server is in
faily good shape to prevent any
"basic" misuse.
I have looked for articles online and SO but have not found much in terms of keeping the private key off the server completely. Even if I need to keep on the server itself - thoughts or suggestions for how to move forward are appreciated.
EDIT - CLARIFICATION
Just to be more clear, the goal I am trying to achieve is this (in very basic form):
Customer enters his/her phone number
online.
The phone number entered is encrypted
online using key A and stored within
the mysql db
The customer will never be able to
see the full phone again at this
point, but can certainly update it
(going through key A process a nth
time)
As a system administrator, I am only able to access the data by either downloading and decrypting the data on my local machine (that or I must first upload a temporary file which is used to then decrypt the data I need).
EDIT 2 - I'm a an idiot
I am using Andrew Cooper's response below but am having trouble getting my script to read the contents of the .pem file I generated. Based on the code below - how would I get $public key to correspond to a specific .pem file on my server?
<?php
if (isset($_SERVER['HTTPS']) )
{
echo "SECURE: This page is being accessed through a secure connection.<br><br>";
}
else
{
echo "UNSECURE: This page is being access through an unsecure connection.<br><br>";
}
// Create the keypair
$res=openssl_pkey_new();
// Get private key
openssl_pkey_export($res, $privatekey);
// Get public key
$publickey=openssl_pkey_get_details($res);
$publickey=$publickey["key"];
echo "Private Key:<BR>$privatekey<br><br>Public Key:<BR>$publickey<BR><BR>";
$cleartext = '1234 5678 9012 3456';
echo "Clear text:<br>$cleartext<BR><BR>";
openssl_public_encrypt($cleartext, $crypttext, $publickey);
echo "Crypt text:<br>$crypttext<BR><BR>";
openssl_private_decrypt($crypttext, $decrypted, $privatekey);
echo "Decrypted text:<BR>$decrypted<br><br>";
?>
EDIT 3 - maybe not 'idiot' but semicolons hate me
I had a semicolon misplaced. I am using the function: file_get_contents() but is there a more preferred method of reading in the data for the .pem file?
You should be able to generate the public/private key pair on your personal machine, and then publish the public key in your app so the data can be encrypted. In this way the server never sees the private key, and if the server is hacked the data is still safe.
You'll want to make sure the whole transaction occurs over SSL. The client side can generate a random session key, encrypt the data with that key (using AES), then encrypt the key with the public key from your app (using RSA), and send the encrypted data and key to the server. You could store the whole blob in one database field or two. The only way the data can be decrypted is to decrypt the key first, and the only way that can be done is by using the private key on your personal machine.
Update
Check out http://plugins.jquery.com/project/jQuery-Gibberish-AES. It's a JQuery plugin that appears to allow this type of scenario. I have no experience in using it, but it appears to me to be a good start.
New Update
Just to be clear about what I'm suggesting, and to address your edit:
You can't use only AES encryption. With AES there is one key that is used both to encrypt and decrypt. The key would have to exist wherever the encryption operation occurs, either in the client code, or on the web server. In the first case anyone can get your key. In the second case, if the web-server is compromised, then the key, and the data, are also at risk.
The solution is to use good, strong AES encryption in combination with public-key crypto (RSA). I'd suggest doing to the crypto on the client-side, for reason I'll outline below. Here, though, are the steps I'd suggest:
On your private machine create a public/private key pair, and keep the private key safe.
Put the public key somewhere in the code you send to the client.
When the user submits the form the client code:
Generates a random AES key (the session key)
Encrypts the form data
Uses your public key, and the RSA algorithm, to encrypt the session key
Discards the plaintext session key
Sends the encrypted form data, and the encrypted session key to your server
Your server accepts the encrypted form data, and stores it, along with the encrypted key, in the database.
You now have encrypted data in the database that can only be retrieved using the private key stored on your private machine. Even if the user somehow manages to capture the session key while it's in the clear on his machine, the worst that can happen is that that one record could be decrypted.
The reason I'd suggest this client-side approach is that it means that your server never see the encryption keys in the clear. If the same scheme where employed on the server-side then, theoretically, an attacker could be sitting on your server watching it happen. At the end of the day it basically comes down to how paranoid you want to be.
Following this scheme, when you want to retrieve the data you'd dump the required data, in encrypted form, from the database to your private machine. The for each chunk of encrypted data:
Decrypt the session key using the RSA algorithm and your private key
Decrypt the data using AES with the session key from step 1.
Anyway, that's the approach I'd suggest. I'm sure there's libraries out there to handle this.
Encrypts that data using AES and a "public" key
...
decrypt the data ONLY with a private key
But AES is a symmetric encryption algorithm - i.e. the same key is used for encryption and decryption.
Or do you mean you want to implement something like SSL, where some assymetric algorithm is used for encrypting a randomly generated key then the end points use that key for a symeetric algorithm? This kind of approach is only of benefit where the data to be encrypted is significantly larger than the keys used - is that the case here?
Have a google for PHP and RSA or ELGamal for assymmetric encryption algortihms. (note it'll probably be significantly faster and easier to program if you shell out to something like GPG to do the encryption - there are wrappers on phpclasses for this).
C.
... That doesn't strike me as possible. MySQL's AES_DECRYPT method requires the encoded message as well as the original key in order to decrypt something. This means that anyone that can get the encryption key can decrypt the message.

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