So I am encrypting data, storing it in the database, and decrypting it, using mcrypt.
I am wondering if it's safe to store the key for encryption in a php file outside of the public_html directory?
The reason for storing it in a file is that it needs to be used for multiple encryptions, so that multiple users can decrypt some data, and I figured storing it in a file is more secure than in the database table, right next to the encrypted data.
What are ANY potential security risks? Is it at ALL possible for a hacker to gain access to this file and thus the key?
Storing it above the public_html is a good idea. Your file should have the correct permissions configured so that only the web server or users that require it can read it.
An option is to split the key up and store in different places, for example part of it in a file on the file system, and part in the database. The benefit of this is it's harder to get the full key for an attacker because they need to access both the file system and the database.
Also consider your server environment has an affect on security, for example shared hosting is less secure than a dedicated server.
No one can say that it's impossible for an attacker to access the key because that depends on your entire server setup and config. Server's are most often compromised through vulnerabilities in software such as web servers, so you should follow good security practices such as keeping your software up to date.
If your server (as in its OS) is compromised, it is "game over", no matter whether your key is stored in a file or the database. So yes, it is "at all possible for a hacker to gain access to this file and thus the key" - by breaking into your server's OS.
If apache or PHP are compromised, but not the OS, you end up in a chicken-and-egg problem: If you put your key somwhere, where apache/PHP can access it, it can be taken by whoever breaks into apache/PHP. If not, you can't use it in your webapp.
This leaves only a scenario, where your webapp is compromised, but not the surrounding infrastructure - in this case, a file might indeed be a good idea: Many break-ins (e.g. most of the SQL injection variant) gain access to the DB, but not to the file system.
For sensitive environments we sometimes chose a model, where encryption/decryption is handled via a pair of FIFOs, with the real crypto being done by an external process - this can do some heuristics and refuse decryption on suspicious patterns.
Related
I am using PHP's openssl to encrypt sensitive user data on my website. I have an ssl certificate to provide further encryption. But how do I keep the key secure?
I have done some research and come up with these steps to follow-
1) Store it in another server. Away from the root folder.
2)Some row specific keys are kept in the database, but encrypted with a master key.
3) The master key isn't in the stored in the drive but rather kept on the ram.
4) Proper authentication of the key before usage.
But how do I ensure no one gets the master key?
I have 2 servers- A and B.
A contains the protected database, B has the master key. Now, A, when necessary will retrieve the key from B and store it in the ram, preferably as a variable.
How do I ensure no one gets the key at server B? Should I store it as a php file or some other format? If it's another format, how do I make sure only server A's decrypt.php can only read it and no one else can?
Thanks for the help!
Keeping an encryption key secure is a very difficult problem. Generally the best you can do is make sure it is not in the http path and make the server as secure as possible. That includes two factor authentication and careful control of the second factor.
The only way to really keep the key secret is to insure it is never available and that would require a HSM (Hardware Security Module), a separate hardware device that performs the encryption/decryption so the key is never available. Think $5,000+.
You can take a look at AWS KMS or if you want to store some secrets on local premises, have a look at this open source project - Hashicorp Vault - it is pretty nifty.
What are you trying to protect your data from?
If you are trying to protect it from a SQL injection attack, for instance, then having it encrypted "inside" the database may offer some help.
If you are trying to protect it from someone who is attempting to gain privileged access to your web infrastructure it is an entirely different issue.
For example, if your servers have not been hacked and the code residing on the server has not been compromised, then if the software is doing it's job you'd be in a situation such that the software will decrypt only the data that the authenticated user is allowed to decrypt.
Generally, if someone gets privileged access to your server/code it may not make much difference where the key is stored. As noted by Zaph, leaving the web server unaware of encryption particulars by having a less accessible box sitting between the web server and the database may allow a higher level of security.
I'm doing something similar and will be using an AWS VPC to isolate sensitive/admin capabilities away from the web server. KMS will be used to keep the key encryption key opaque. Various types of monitoring will be performed and in the event an issue is detected the KMS key can be disabled - preventing access to data until the issue is resolved.
In my database I store information which is encrypted and decrypted on the fly via a PHP-class.
Per application I use a private key appended with a user key to make sure the decryption only succeeds when a user tries to decrypt its own data.
The user 'key' is stored in the database; but the private key (application level) is stored as txt-file in the FS. Off course 'above' the web-root.
Considerations:
- If the database gets hacked: they end up with one part of the key, and encrypted data
- If PHP-stops or is corrupt: they end up with a single page with only include('../private/private.php') in it.
- If NGINX fails: the connection is 'just' dropped.
The only scenario I can think of is the corruption of the system itself. But the server runs a firewall, is updated regularly, runs fail2ban and only the services needed are run. SSH logins only via key-access etc etc
I was wondering if this is the 'best' practice.
Or if there is a better way to do this kind of encryption with above specifications?
What would be the correct access-rights to the key-file?
At the moment the database and webserver are both on the same server facing the internet. Is it better to split them and create an internet facing server with only the webserver; and put the database server and key-file on a different server in a private network?
edit: the private key to encrypt the data is build up by two components:
$key = $app_key . $user_key
I was wondering if this is the 'best' practice. Or if there is a
better way to do this kind of encryption with above specifications?
What would be the correct access-rights to the key-file?
Hard to implement
To be perfectly secure you would need to generate a private-public key pair on an HSM (hardware security module) such as a smartcard or an HSM box. The generated private key is created inside the HSM and can never leave the HSM, it can only be used for decrypt / sign operations inside the HSM itself, however it can never be read off the HSM. In this case if someone hacks your server, or even get complete control over your server, they can never get hold of the private key stored inside HSM. Depending on number of decrypt / sign operations required on your server you would need a reasonably performing HSM to decrypt required data. Accomplishing this task in pure PHP is however a long way to go ( you could implement your own C++ extension or use inter-process communication with a process that is able interact with HSMs)
Easier to implement
It is important to harden your server and minimize the attack vector as much as you possibly can. This mainly includes assigning appropriate permissions to files and database. Have a look at this for some good security practices.
To add my few cents you can use:
APC for in memory storage of your private key in php (this will also give you a boost in performance in compare with constanlty reading a key from a file). You would need to implement some secure mechanism (such as you entering a passcode ) to decrypt /restore an encrypted key into the APC every time you restart the web server.
Something like ionCube for encrypting and protecting your PHP scripts
Control you deployment process using an application container such as docker
At the moment the database and webserver are both on the same server
facing the internet. Is it better to split them and create an internet
facing server with only the webserver; and put the database server and
key-file on a different server in a private network?
Yes, separating the webserver from database server is definitely a good practice.
You can load the key into APC or something similar. This would require human input during boot (or perhaps kicked off from a more secured server). This would mean the key is never stored in any file on the server.
It's not perfect, but it's better than the more typical approach of using a local file.
q.v., Where should I store an encryption key for php?
Look at the OWASP Cryptographic Storage Cheat Sheet and its recommendations. Rule 2.1.5.4: Protects key in a key vaults. Keys should not be stored on the application or web server.
I understand the concepts of securely storing data for the most part, including storing the data on a separate server that only allows connections from the application, key-pairs for encryption, etc. However, I'm still not understanding how separating the server makes it that much more secure.
For instance, suppose I have a web server, which is hardened and secure, and it captures the data from user input for storage. The data is encrypted and submitted via a db query or web service to the db server. The db server only allows connections from the web server and stores the data in an encrypted form. Therefore, if someone access the db, the data is worthless.
But, if someone access the web server, they will have access to the db as well as the encryption algorithm and keys, no? That being the case, why even have the data on a different server, as the transfer of the data is just another potential point of attack?
Is there someway to hide the connection information and encryption algorithms on the web server so that if it is compromised, access to the db server is not gained? Obfuscation isn't enough, I wouldn't think. Any ideas are welcome.
Thanks
Brian
There's a certain amount of magical thinking and folklore in the way people design for security, and you're right: storing data on a different server on its own doesn't necessarily make things more secure unless you've done all sorts of other things too.
Managing keys is a huge part of this; doing this in the context of web applications is a subject apart, and I'm not aware of any robust solutions for PHP. You're quite right - if your web application needs to be able to decrypt something, it needs access to the keys, and if the web app is compromized, the attacker also has access to the key.
This is why I've tended to use public key cryptography, and treated the public facing webserver as "write only" - i.e. the web server encrypts using the public key, stores in the database, and can never decrypt it; only a separate process (not available on the public internet) can use the private key to decrypt it. This way, you can store credit card details in your database, and only the application which charges the card has the private key to decrypt it; this app runs on a secure environment, not accessible from the internet.
Secondly, there are multiple levels of compromise - for instance, an attacker might get read-only access to your server's file system. If that file system includes the database, they could get hold of the data file, restore it to a server they control, and use the decryption key to steal your private data. If the database runs on a separate server(inaccessible from the internet), this attack route becomes impossible.
The fact that one route of attack leaves you open doesn't mean you can't protect against other attacks.
In most of my setups, the web server is in a DMZ of the firewall, and the DB is behind the firewall. I would never want to put the DB server outside the firewall. That extra level of security makes it much harder for someone to get to the data without authorization.
BTW, no web server on the net should be considered "hardened and secure". If it's available to the public, it can be hacked. It's just a matter of how hard they want to try.
You're right in your assumption that if someone hacks the webserver to the point they can log in as an admin, they can read and write the database. But that doesn't mean you should further weaken your setup by putting the DB on the web server. You want more security, not less.
EDIT:
Always think in terms of layers in your security. Separate critical parts into separate layers. This does two things. It makes it where the perp has more problems to solve, and it give you more time for detection and response.
So, in your scenario, access to the web server is one layer, you could then call an encryption server for a second layer (behind the firewall, which is another layer), and the encryption server could be the only machine allowed interaction with the DB server, which is another layer.
Layers make it more secure. They also, though, add burden, slowing the response time. So keep your solution balanced for your real-world requirements.
The problem here is that the keys are on the publicly-facing server which could be compromised - even if the server itself is "hardened", there may be a vulnerability in your app which gives an attacker access to keys or data.
To improve the security of your arrangement you could move just the code that handles encrypted data (along with the keys) onto a secure machine that can be accessed only by the web server, and only through a very restricted API (i.e. bare minimum that is needed). Each operation is logged in order to spot unusual behaviour, which could be symptomatic of an attempt to extract the secret data.
From a security perspective, putting the database into a separate server doesn't really help. If authentication tokens get compromised, it is game over.
However, it does make sense to separate database AND data access layer (DAL) from business logic and presentation. That way, if the application server falls prey to unscrupulous hands, database access is restricted to specific DAL operations which can go a long way of putting data out of harms way if properly implemented.
Other than that, there isn't much of a security benefit in segregating data storage into a separate server.
I'm in the process of prototyping a file upload system for a service that needs a basic form of encryption for the files uploaded by users. All of the files uploaded will be uploaded to the same directory in which users can download and upload files freely, however only authorized users will be able to delete files from the uploads.
With this in mind, I need to know the best way to encrypt these files (via crypt() or similar) for storage in a non-public-accessible directory for this purpose. I considered using the base 64 encode functions built into PHP to do this, but it seemed as if someone would be able to write a PHP script on another server to base 64 decrypt the files stored on my server, thereby rendering the encryption protection completely useless.
In summary, I need to know the best way to implement this (i.e. which functions or classes to use) so that it meets the following criteria:
The function needs to be reversible in a way that only users logged in via PHP's $_SESSION variables can decrypt the files that are encrypted.
Encryption needs to affect all file types, whether it be images, text, binaries, documents, and decrypting the files must yield a file identical to the file that was encrypted (i.e. with the header intact).
I may be worried about more than necessary, but I would like to make this as easy to use as possible with basic security. I'm not protecting anything particularly important such as credit card information or trade secrets, but the users I am designing for would like to have the peace-of-mind to know that there are at least some measures in place to prevent hacking of the files uploaded.
base64 is not encryption.
If you want strong encryption, you'll want to think about using the GPG extension
Then you need to carefully consider your system architecture.
If the server needs to encrypt/decrypt files, the key will need to be on the server, and readable by the web server process. This means that if someone compromises your server, they probably have access to everything they need to decrypt your files!
Even worse, since your PHP app drives the encryption/decryption, all an attacker needs to do is get access to one of your users' accounts.
Encryption is only going to save you if an attacker somehow gets access to your file storage directory, but nothing else on the server. That's a very unlikely scenario.
In your case, it sounds like on-disk encryption is overkill. If the files are not directly web-accessible, that's probably enough. Focus instead on making sure the host system is secure (updated packages, good firewall rules), and that your application is secure (run it only under https, use best practices to defeat SQL Injection and CSRF attacks, require strong passwords, etc).
Scenario: a web application written in PHP utilizes an Amazon Web Service and must keep the Access Key ID and a Secret Access Key handy in order to function. Are there current recommendations and/or API's out there for storing this data securely?
My thought is to symmetrically encrypt it into a file based on a key created from local server variables. That way it's [hopefully] gibberish if someone gets a copy of the file through FTP, lost laptop with files copied, etc. The concern I have is that a skilled attacker could just upload their own script to decrypt it.
This seems like a common situation and one I've never achieved a comfortable solution for. Obviously I can't use a one-way hash, because I need the original data to create a HMAC to send to AWS. Links to related S.O. questions are very welcome.
Ah. The question of security.
I think the question you should be asking here is what do you do with say, for example mySQL passwords in your php config files?
To be quite frank, I would say that if someone managed to get a copy of your files, then your security needs rethinking anyway. For my own use, I generally only keep the passwords in one place, (on the server where they should be used) and make sure that I use a randomly generated password each time (paste it into the config file, and voila!)
To be honest, if it's not your own host, ANY sensitive data can be compromised.
If it is your own host, I'd suggest using proper permissions within Linux, and PHPSuExec to make sure that only the scripts that YOU write can access the files.
Anyway, to answer your original question, your AWS Access / Secret Keys are just the same as a MySQL password, Ok, it has the potential to let someone access your service, but it doesn't give them access to your personal details. Even with symetric encryption, if your script has a security hole, the information can be accessed.
Put simply, you take a risk when you put these keys anywhere that is accessible to anyone but you. How much do you trust Amazon's servers not to be compromised?
My suggestion would be to try and add as much security as you can, but keep an eye on your account, I'll generally have a cron job running to send me an email with changes to my S3 account (new files uploaded, new buckets etc etc) and from that I can tell what's going on.
There is no easy solution, it's a mix of securing each seperate layer of the System. I mean, if you use symetric encryption, the password for that has to be stored somewhere, right? or are you going to type it in every time ?
Hope this helps
My thought is to symmetrically encrypt it into a file based on a key created from local server variables. That way it's [hopefully] gibberish if someone gets a copy of the file through FTP, lost laptop with files copied, etc. The concern I have is that a skilled attacker could just upload their own script to decrypt it.
This wouldn't hurt, but ultimately it is just security through obscurity as somebody who can read the file can probably also read and reverse engineer your code. Short of typing in a password or otherwise providing a secret every time the server starts, encryption isn't going to help. It just shifts the problem to how will you protect the encryption key (which also needs to be accessible to the server)?
You have to harden and design your application and server (don't forget the OS, and remote access to the OS) so that nobody unauthorised can read the files on the system in the first place.
If you're worried about someone getting physical access to the box, concentrate on physical security to stop that happening.
I use symmetric encryption like you suggest. When I start my server I need to give it a key to decrypt the files containing the authentication data.
Of course a hacker could do a memory dump and read the password that way but that's quite a bit tougher than reading a cleartext file. There's no perfect solution, security is always a compromise between risk and ease of use.
So server security is still the key issue, its just a question of how much security is enough. I'd suggest looking at Bastille Linux or something like that to harden your server but that's another topic altogether.