Appropriate encryption for financial data [closed] - php

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 5 years ago.
Improve this question
I'm currently building a web application(PHP/MySQL) which saves data from persons. Most of this data is not worth protecting with encryption but some of it is financial information like income and so on. It is not a payment application and does not store information that could be turned into money directly like credit card information but still stuff you don't want to have in a possible leak. This platform has to be sold to customers that want "security", but that can mean anything as the customers themselves do not have any knowledge what they really want, since they are business people and not cryptographers(like me neither).
It is a management platform so the people that have their financial data saved there are not the users of the platform. Users of the platform are merely a login with permissions attached to them. The server itself never has to have access the data. Every operation is done by a user(could also be an admin) that is logged in. Multiple users need to have access to the same data given they have enough permissions.
My question is now how I can protect the financial data from these threats:
Somebody finds an SQL-injection and dumps all tables remotely
Somebody steals the hard drive of the server (database + code)
Where I'm certainly not going: Large scale sniffing attack or compromised servers(like sniffing all traffic on the server itself where SSL doesn't matter) or social engineering/phishing.
I would also like to have a quick summary how much more information(keys, data, etc.) I have to store in comparison to the current system, where there is one simple field for income etc. and a standard login system with username and hashed password.
EDIT: Reformulated question almost entirely following the suggestion of comments/answers

Here are two approaches to this:
1) Use symmetric encryption because you have already arranged a secret with the client, which is their password.
Whenever the user requires access to their sensitive information, they need to provide their password. If you require this, then you can use that password as a basis of generating the encryption key.
You can use the openssl functions in PHP to encrypt the sensitive data, and decrypt it when the client needs it. This will allow you to select an appropriately hard to break algorithm which OpenSSL supports. The drawback of this is that you will need explicit user permission and their password to access that data, which is good if you're only storing it on behalf of that user, but bad if you need to pass it on to someone else.
This way you will not need to store additional information in the database. In case someone steals your hard drive, all they will have is encrypted sensitive data and hashed passwords. The drawback is that it's a single point of failure, if they break the encryption they also get the password and vice-versa however the difficulty of breaking the encryption is not as high as reversing a hash. It also relies on strong passwords, which as we know users often don't tend to use, however that's not a new problem and one we're not likely to solve today.
2) Require the user to generate a private-public key pair and send you the public key. You can then store this public key and encrypt data using it. This generally would work well if you had an app/software that communicates with your server, which can do this on the user's behalf, but is harder to implement in a web application. Perhaps there's JavaScript libraries that can do this but since it's not something that is commonly done you need to be 100% sure the library you're using is secure. However this also requires of the user to store the key somewhere and be able to use it whenever they want access to that data (again JavaScript can do this for the user but saving and loading the key is something that requires user interaction due to security concerns).
In short:
Symmetric encryption would only be secure if the encryption key is not stored on the server but is something that the user can provide whenever it is needed.
Asymmetric encryption is even more secure but unrealistic in a web application targeted to an average user.
So I would suggest symmetric encryption using the user's password as a key.

From your question, the following key points stand out.
The server itself never has to have access to the data.
Multiple users need to have access to the same data given they have enough permissions.
Maintain security even if:
Somebody finds an SQL-injection and dumps all tables remotely.
Somebody steals the hard drive of the server (database + code).
This is possible to achieve, but not trivial. The thing that makes this possible is the fact that the server does not require access to the data. This allows us to use user passwords to derive keys.
Each level in your permission structure will have an associated key. This key will be used to encrypt data that can be viewed with those permissions. When the first administrative account is created, generate a key for each level in your permission structure and use the administrative password as an input for a KDF and derive a key. Use this password-derived key to encrypt each permission key and store the resulting ciphertexts alongside the administrative account.
As new users are created and assigned ranks by the administrative account, pull the highest level permission key that the new user will have access to, as well as any keys at a lower permission, decrypt them with the administrative password (which will be required for creating users) and then encrypt them again with the new users password and store alongside the new user in the database.
This system allows you to pass the required encryption keys to each user and makes accessing data above the users permission level cryptographically impossible.
At this point, it is rather straight forward for you to allow users to access data by simply taking their password, decrypting the relevant permission key and then using that key to decrypt the data. Users changing their password is also trivial as it simply means you have to decrypt the permission keys with the old password then re-encrypt with the new password.
At a more technical level, I would recommend the following:
Use AES. AES-256 tends to be the most common but AES-128 is just as secure in the grand scheme of things. Use of an authenticated block mode (GCM) isn't as important here but is still recommended. If not, use a mode like CBC or CTR with an HMAC.
Never use a password directly as a key. Use PBKDF2 to generate keys from passwords. Using AES-256 fits in nicely here because you can use SHA-256 as the primitive to PBKDF2 and get output the same length as the internal hash function.
Generate a new random IV every time you encrypt using a CSPRNG. Prefix the IV to the ciphertext. Don't derive an IV from PBKDF2 like the key.

Asymmetric encryption and hybrid encryption are pointless here unless the users generate and retain ownership of the private keys themselves. I infer from the rest of your question that this isn't the case.
Assuming you want to be able to view this encrypted information without user interaction (e.g. you aren't just storing this information for the user and the information is relevant to your business operations), you have limited storage options.
If your exact threat model is to protect this data in the event of a database leak and nothing else, symmetric encryption is perfect, if properly implemented.
The implication of this is that the symmetric key must be stored on servers that make requests to the database and serve the data to your other (likely front-end) systems. If any of those servers were to become compromised, then the encrypted data will be leaked.
In summary, use symmetric encryption, but understand that it will only protect you directly from a database leak through something like SQL injection or a similar attack. A compromised server is a compromised server and generally means full data access given enough time.
EDIT: If you intend to require user interaction to view the secured data, then apokryfos's comment above accurately details what to do to secure the information. Generate a symmetric key from the users password and use this to encrypt an additional symmetric key. Use this secondary symmetric key to actually encrypt the data. Using two keys makes a user password change easier.

Related

Only require password once to decrypt files at different times?

User's content is encrypted, but needs to be decrypted. There are multiple files that need decryption to be viewed, and they will definitely not be viewed at the same time.
I am currently encrypting by using the user's plaintext password to encrypt a randomly-generated key, which encrypts the user's data. The password is hashed and verified normally before doing anything. I am using PHP's aes-128-gcm openssl_encrypt() function.
My current system requires a password every time the user wants to read a file.
I have thought about decrypting all of the content at one, but this doesn't scale well. I have also thought about storing the user's key as a cookie, but I'm worried about security.
Is there a standard way to do this?
Thanks!
The first thing to do is separate the users password out of this. You'll have to decrypt and re-encrypt all their files. There may be other ways around this such as allowing only new files to use this system. But that is very use case specific, such as how long do you keep their files, what is the turn over on them etc..
In any case this is a way to do that:
Encrypt the files they submit using a password you generate.
Store this password in another file we'll call it key.txt for now. Encrypt this file using the users password.
When user logs in (if they don't have it stored) take their password, decrypt key.txt and get the generated password.
Now you can save this generated password anywhere you want, without affecting the users account.
What they see (the end user experience) will look like always they go to downlaod a file, put their password in and get the file. They wont ever know you did this, which is nice for them.
So problem one is fixed.
Now where should we store this?
You could simply store it on the server in the DB. This sort of depends on how confidential the data is, and how secure your server is. Your ultimately responsible for the security of someone else's data, at least this way you can control it.
Make a table with these fields
user_id | ip | password | last_access
When a user goes to download a file, check their last access time and IP address to invalidate the password and make them refresh it. This is very easy to setup and totally under your control. If you save the encryption key, it will always have some level of vulnerability at least this way its all under your control.
Even if you don't want to store it in your DB, the biggest disadvantage here is if someone gets a hold of that table, but if they do that and your storing important data you probably have plenty of problems already.
At least use the first part as that solves a big problem with tying this to their actual account password. Even if a hacker gets the file password from the client (stolen cookies etc.) because it's separate, having that alone wont let them login to your site like the account password would. I am assuming here, a user must login to even get to the download part. Using the same password for both gives them them access to both the means of the getting this data and the method to download it.
To be clear, their is an argument to be made about storing it on the client side. Then if your site is compromised there is less chance someone could get a hold of the password as it (depending how you do it) only exist in memory on both the client and server etc. It puts the responsibility on them.
ASYMMETRIC ENCRYPTION
You could also use asymmetric encryption. Currently it looks you are using AES, which is fine, but it's a Symmetric Key block cypher. Basically there are three common forms of "encryption" (in vernacular):
Hashing (which really isn't encryption) - md5, sha1, sha256 - these are one way, can't be decoded. They have fixed lengths, and always encrypt to the same thing. It's common to see this for file checksum (for validating the contents of the file), Block Chain, Passwords or anything else where you need to compare two "encrypted" values.
Symmetric - AES, 3DES, Blowfish, Twofish - anything you need to encrypt and decrypt. The same key can do both. Generally these will encrypt the same thing to different values each time, because of the IV.
Asymmetric - SSL, DSA, RSA, PGP, used in Crypto currency wallets, TLS etc. With these you have 2 keys, a public one and a private one. The keys cannot decrypt their own encrypted data, only the other key can. So with this if you have one key on the server and the client has the other. You can encrypt their files using your key (decryptable by only their key) and you don't have to worry so much about someone getting your key as it won't allow them to decrypt the files. You can give one key to the client, who can use that key to decrypt their data you encrypted (even without your key). These also encrypt to different "Stuff" each time you use them.
So you can see Asymmetric form has a few advantages to use in a two(or more) party system. It also has the benefit that you don't need their key to encrypt a file. All you need is your part of the pair. So for example if you generate data for them and wan't to encrypt and later have them decrypt it with the same system, you can do that with no issues. This probably eliminates a step, as you would need to ask them, or keep track of their Symmetric anytime you wanted to encrypt something. Here you just need your part of the key pair.
It really isn't much harder to implement (on the server), its just harder to understand what it does. That's why I decided to add this, without this knowledge (which you may or may not already know) it's hard to use these terms and have them make sense. The only real disadvantage for you (if you call it that) if you used Asymmetric encryption, is if a client loses their key you would have no way to decrypt the file. So I would make sure they know to back them up in a secure place. It's the same problem that you see in the news when it comes to losing a crypto currency wallet which is encrypted Asymmetrically
As I said most of my knowledge has to do with encrypting and dealing with data on a server. So I am not sure how to tie that in to the "client experience". I do know for example how to use RSA keys for password less login for SSH etc. Which is kind of the same thing but not quite.
Hope it helps!
they will definitely not be viewed at the same time
Wouldn't the most secure answer here be to simply require the password every time? I would assume (although I'm sure this isn't the answer you're looking for) that simply asking for the password each time might be the best solution.
Although it may be tedious for the user, I would also assume it imparts some sense of security - since it's not quite as simple as logging in (as the files are encrypted).
From my perspective, I would argue that encrypted files should not be mass decrypted anyways?
Sorry, I know this isn't the answer you're looking for - but if you have more information about your motivation, maybe then a more reasonable solution can be found?
Don't do decryption on the server-side - do it client side. It is safe to keep the user's password in memory on their own device.

Making a six digit pin system more secure at the back end

I'm developing a password manager which has two forms of authentication.
First the user logs in using their password. I have this working securely using bcrypt.
The next part is far more tricky. The user's saved services are stored in a JSON format like so:
{
"name":"facebook",
"login":"myemail#email.com",
"signup-email":"myemail#email.com",
"password":"121654754321",
"notes":"my security questions answer is \"blah blah blah\""
}
I plan to store this in a DB table with two fields: id, and data(the AES256 encrypted JSON).
So far my design is using a 6 digit pin with a 32 char salt (unique to each user) to encrypt this. the pin is never stored. the salt is stored in the user table.
What are the possible flaws in doing it this way? Can anyone recommend a better way to implement this?
The risk
Your systems poses a great security challenge, since it acts as a central password repository, thus making it a choice target. Your best defence is ignorance: if you can't decode it, an attacker would have a hard time doing it too. Also, you don't need access to the data stored, but the user (and only the user) does.
What you've done/intend to do is on the right track since you don't have access to the full key. But why do you use a 6-digit pin instead of a "standard" password/phrase? You're responsible for the security of over 80% of the encryption key, and you've got all of them in one single place! An attacker just needs to figure out the (quite weak) pin to add to the salt in order to read the data. Plus, if your system gets compromized, one could manage to retrieve user requests in order to extract the pins and gain access to your user's accounts.
(some) Tips
What could you do then? I'm no expert in security, but I know some tricks. I'll try to answer to the best of my abilities.
Delegate decryption to the end-user
If the decryption key never gets close to your system, the data would be way safer. In order to retrieve a user's passlist, one would need to break into this user's computer and somehow retrieve the decrypted data off the page. That's totally doable, maybe easier than breaking into your server in some case, but this would only grant access to only one account. That's quite good for your other users :D
There's a growing number of applications using this approach to acheive similar results. You often see client-side AES decryption done with JavaScript for instance. The zerobin project describes such a mechanism.
Use strong encryption keys
As said earlier, leaving the user only 6 bytes in the encryption key isn't really robust. They need to be able to add as much entropy they like. Passwords with an upper size limit are often a bad idea (I personally hate those who tell me how short my password has to be).
Have a look at key stretching
(This point is related to the previous)
Key stretching describes an operation taking a standard "human" password and transforms it into a stronger encryption key. It has many applications, and is in use in several password managers of the kind you're building.
RSA's PBKDF2 is a well-known key stretching algorithm used in many security applications.
Side notes
Of course there are several other points you need to address in order to build a secure system:
Obviously, you NEED to enforce SSL (HTTPS) communications between your server and your clients ;
Access to your server needs to be well protected in order to protect the secrets used for encryption. Client-side decryption greatly reduces the threats posed by break-ins, but that doesn't mean they shouldn't be protected. This implies fine tuned firewalls, up-to-date applications, reactivity to security fixes, etc. ;
You need to teach a thing or two to your users about security. It's well known that both ends of a channel have to be secured in order for the channel itself to be secure too.
There are probably many other concerns, but hey, this isn't a lecture on security ;-)
The pin is known and inserted only by the user? Instead of using the 6 digits pin + salt for encrypting the data you can use PBKDF2 (Password-Based Key Derivation Function) to generate a secure encryption key based on that 6 digits pin (and the salt). This way each user will have a different (and secure) encryption key. Mind that along with this you should always use a different and cryptographically secure generated IV that can be appended (or prepended) to the encrypted data.
The security flaws of your implementation are that there is the possibility of not having enough entropy generated by your pin + salt and since you didn't talk about an IV I suppose you are not using one thus exposing possible patterns in your encrypted data.
For the security flaws my implementation has instead, depends on what are your security objectives. For most people PBKDF2 (+ one of the hash_algos() algorithm as per the PHP implementation) is secure enough, even if it is more exposed to attacks using FPGA or GPU clusters. If you want to avoid them, you can use bcrypt to generate a key that is harder to attack using these technologies.
I recommend you to read this answer that gives more in-depth reasons on why bcrypt is somewhat better than PBKDF2

Where to store encryption key?

I'm currently finishing up a php program that backs up multiple Google Calendars which requires me to store email addresses and their passwords. I'm using AES encryption (I'm not using a hash because I need two way encryption) and although all passwords are encrypted with the same key, each password has its own iv which is stored in the database with it.
I know there is no 100% guaranteed way to keep the data safe if someone can gain full access over my system but I was hoping for some suggestions of storage methods/storage places that makes life difficult for potential hackers. Currently, I've considered putting the key in a file only the sole administrator has access to or setting an environmental variable.
I've considered putting the key in a file only the sole administrator
has access to
if the system would be compromised (or having physical access to your hd), than an attacker can gain the access to your secret key.
I think the better solution is to hold somewhere in the fs an encrypted version of your key, protected with a passphrase to be entered manually everytime your secret keys are needed. Something like gpg key-ring.

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