I am going to use AES_ENCRYPT() and AES_DECRYPT to store patient data in an EMR System, and I was wondering how to store the key. I need to be able to allow authorized users access to that key in order to decrypt and read the data that is stored in the patient's record. How can I easily share a key with many users, but keep that key secure. Any thoughts or examples are appreciated.
The standard way to do this is to create a "system" key for each patient, and use that key to encrypt that patient's data. Do not share that key with the patient, or with any of the users.
When a user enrolls in the system, use the password to create a "user" key for that user. (For example, take the SHA-256 hash of the user's password.) Do not store the user's password on the system.
Then, when the user is authorized to access a patient's data, encrypt that patient's system key with the authorized user's key and save the encrypted key in the user's account. If the user is authorized to access additional patients' records, repeat this process for each patient's system key.
Finally, when an authorized user wants to access the patient's data, s/he enters his or her own password, which is used to decrypt the patient's system key, which in turn is used to decrypt the patient's data.
When a user wants to change his/her password, you must decrypt each of the system keys that are associated with that account, and then re-encrypt them using the new password.
Disclaimer: Security is Hard* and I am not a cryptographer. In addition, the law may require that patient data be encrypted and otherwise protected according to certain standards. Before you implement any security system, consult an expert, and never under any circumstances create your own security scheme, especially where financial, medical, or other critical information is concerned.
*Bruce Schneier, Chief Security Technology Officer, BT
Related
I am building an Electron app and implementing Cloud Storage support. Users can upload files within my app to their account. Me, as an admin, I don't want to be able to read the files through the Firebase admin console. I also want to avoid a user password as people might forget it. Just logging into their account should be enough to access their files.
In my prototype I store user files in data/${user.uid}/. But now I am stuck and don't know which password I should use to encrypt the files.
There are a few questions around this topic which involve DigitalOcean which looks too overkill for what I am doing. Is there anything else I could use as a password that is part of the User object that is not exposed anywhere else?
I came across multiple options for Client Side Encryption in File Storage in Firebase. The encryption itself is simple enough to perform with existing libraries, using a symmetric key (a key that can both encrypt data, and decrypt the encrypted data). As the usual problem goes, we now need to find a secure place to store this all-powerful key.
Option 1 : Store Key on User Device
Pros : This stores the key on the user’s device, so the key is never in the application servers.
Cons : The key, and therefore the data, is not accessible from other devices. It’s not a bad solution depending on the use case and situation.
Option 2 : Google Key Management Service for Encryption
Pros : Encrypting the key with another data key stored in Google Key Management Service. The user’s key encrypts the data, and then the key is encrypted by a KMS key and stored in the database. As Andy rightly points out in his blog, that the KMS key belongs to a different Google account to the Firebase database, so no one user has permission to both read the data and decrypt it. A hacker would need to compromise both accounts to access the unencrypted data.
Cons : User has to manage two accounts.
Option 3 : Stash the Key in User’s Google Account
Pros : When the user logs in, we get the OAuth credentials to request the user’s personal encryption key, or create one if we can’t find one, from the user’s Google account. This way, the key is always fully in the user’s possession, but they never have to deal with it directly. Google Drive provides an API for creating a special application data folder (user consent is required during OAuth). The contents of this folder are not visible to the user, and is only accessible via your application’s credentials.
Cons : User has to be cautious not accidentally deleting their own encryption key.
Option 4 : Asymmetric Key Pair
Pros : User first gets the public keys of the recipients. He then generates a symmetric key for himself with which he encodes the file. He then creates a copy of this symmetric key for each recipient and encrypts it with the respective public keys. Finally, he transfers the encrypted copies of the symmetric key together with the encrypted file to the server and stores them there.If another user wants to download the file, he gets it in the encrypted form together with the copy of the symmetric key that is encrypted for him. He can decrypt the latter using his private key and now has the symmetric key with which he can decode the file.
Option 5 : Public and Private Key Encryption
Pros : Create private & public keys for your users when you sign them up. Encrypt data on User 1's device with User 2's public key. Store the encrypted data in your database. When User 2 reads up the encrypted data, his/her private key will be able to decrypt it.
I am working on Encrypting some data using AES 128bit encryption algorithm (Symmetric Encryption Algorithm).
Problem I am facing with this is generating a Key ? As I have mutliple Users and I don't want to share the common key across the users.
Is there is any possibility to generate passphrase in such a way that it is not common to all and can be passed to AES to decrypt/Encrypt the same data?
Example:
lets assume I have a table with employee and their salary. I want to encrypt Salary Column of Employee with AES encryption.
Now when Someone authorized from HR wants to see the salary of Employee they can check, but they should have their own Key (not the common Key).
One possible solution is to create an encrypted version of the master key per user.
So you will:
Encrypt your data with a "master key"
Encrypt your "master key" with a "personal key" (one for each user)
Then, when a user provides its personal key, you use it to decrypt the stored and encrypted master key, and then use that to decrypt the data. This way the encryption for the data can be done with always the same key, and you can regulate access with the personal keys.
This assumes though the master key and encrypted data never leave the server, you will have to decrypt on the server and send unencrypted to the user (but of course use a secure line for that, against eavesdropping).
There is no way to do this if you want to send the data to the user encrypted.
Now when Someone authorized from HR wants to see the salary of Employee they can check, but they should have their own Key (not the common Key).
Using symmetric encryption - effectively there is only a single data encryption key (DEK). The DEK can be random and content specific. You cannot have multiple keys to decrypt the same encrypted content.
What is commonly done using asymmetric encryption, when someone authorizes (shares/assigns) an encrypted content to a user, the DEK is re-encrypted by the user's public key, so only an authorized user could decrypt the DEK and then content (though - the DEK is the same for all users).
when user logs in I will ask them Key
To log in the user anyway needs to provide a secret (its user password or other credentials), so - do you really need to go through all the hustle? Isn't enough to encrypt data at REST with some system-specific master key and provide the encrypted content only to an authenticated and authorized user?
I'm designing an API that uses application level encryption to protect sensitive information in a database.
A simple example of the problem is a user table with the following fields:
UserID
Name
Address
Telephone Number
ClientIdentifier
In the above table all fields are sensitive and should be encrypted apart from UserID which is just the primary key of the table. While the primary key exists for foreign key constraints the actual identifying value for the record is ClientIdentifier. This is an ID for the user controlled by the consumer of the API.
When a consumer of the API wishes to create a user record they pass all the details (including the ClientIdentifier) to us which we store. When they want to retrieve those details they again pass us the ClientIdentifier. For this use case, they cannot use the UserID.
It's likely that the ClientIdentifier could be public knowledge e.g. an email address or account number and can be traced back to a real person. Therefore we have to secure ClientIdentifier as the existence of a record would imply that the person exists on our system.
I see a couple of options.
Hash the ClientIdentifier. The downside to this is that the ClientIdentifier is likely to follow a fixed format and be vulnerable to brute force attacks.
Encrypt all the data but in the case of ClientIdentifier use a fixed IV. The downside here is that an attacker who had access to both the API and the database could execute plain text attacks on the system.
I'm leaning towards to the second option as the plain text attack can probably be mitigated with monitoring of the encryption service, whereas the hashing option could be broken reasonably quickly if a snapshot of the database was lost.
So my question is, do you think I'm on the right track or are there any better alternatives?
Edit: It's possible that we could have multiple records in the database with the same ClientIdentifier. Given a plaintext ID we should be able to select all those records.
If you're set on encrypting both the user id and the password, you might want to take a look at bcrypt. Its an adaptive hashing algorithm, and you can up the encryption complexity whenever you want. You'll have to use a system-wide salt, which will still make this vulnerable to a dictionary attack. Your best bet is to randomly generate an account # (with numbers and letters) to use for the client id, that should negate any dictionary attack. Be sure to make it long enough to be secure.
In general, the user id isn't encrypted because there is no way to salt the hash of the user id, which makes it vulnerable to attacks. Before you jump through flaming hoops, I'd make sure that you NEED to encrypt both the client id and password.
I need to encrypt content in my web application on a per-user basis.
I, the root user, do not want to have access to users' content, period.
How can I make it so users are the only ones with access to their content? Perhaps I can make it so a hash of their login password acts as an encryption and decryption key (then their password is stored one-way hashed in my database, and the encryption/decryption hash is generated from their raw password on login and stored in a local cookie)? But what if they change their password? Then I have to update all their content which could take a lot of processing power.
Is there an encryption method that would provide this, without having to re-encrypt their content if their password changes? Something similar to ecryptfs on Linux, perhaps? Is researching ecryptfs a good place to start?
Is making it so only the user can access their content on my servers (and not even me) even feasible?
Process:
Generate a random secret to encrypt their content.
Using their provided password encrypt the random secret from #1.
Store their password as a one-way hash (with salt, maybe multi-hash).
Upon Password change:
Re-generate the value from step #2.
Re-generate the hash-cache from step #3.
Upon Login:
Hash password and check against hash generated in step #3.
If password matches - use actual provided password to decrypt random secret from #2.
Use random secret from #2 to unlock data encrypted in #1.
Notes:
No one can decode the data without knowing the random secret (#1). Random secret can only be unlocked with user's actual password (#2) (short of brute-force). User's actual password is only known in one-way hashed form (#3) so you can confirm it's the same, but cannot decode it and recover #2.
A forgotten password process is not possible (you can regenerate #3, but random key in #2 is now lost as is everything locked in their vault).
You don't have to re-encrypt everything in step #1 every time they change their password, only the (simple/quick) random secret from #2.
If you cache their provided password, or the random secret generated at step 1, or their (decrypted) content anywhere you could cause data leaks.
You're spot on that you need to use their password as a key.
I wouldn't monkey with ecryptfs because an encrypted file system isn't the best solution. You wouldn't want one user's data to be encrypted with the same key that another user used.
When you encrypt the data, you should generate a random string to use as salt. This prevents someone from using a pre-generated list of hashes to decrypt your data. It also changes the hash of two people who might use the same password.
When a user changes their password, you'll have to re-encrypt the data and generate a new salt value. This is the level of security I would expect as a customer, knowing that when I change my password, I'm re-encrypting all of my data to prevent someone from trying to brute force my key.
You can store the salt value in your database unencrypted.
firstly, I apologize if my question sounds little confusing, I will try my best to describe my scenario as detailed as possible:
I have website where user can input their personal data about themselves. They are mainly health data, so it's very private and sensitive information. So I need to encrypt this data on the server even then the server is compromised these data are secured because they will be encrypted with each user's password. Of course, user passwords will not be stored as clear-type text on the server, only password hashes.
But my problem is that the website will offer "social function" when user can choose to share some of his/her information with another user. But this would be problem, because I will not have any way of decrypting user private data and so I can't show it to another user.
Can you please give me some options, or at least ideas, how could this be solved ? Preferrably using LAMP environment.
This can be solved using public-key cryptography:
Generate a public/private key pair for each user; and only ever decrypt the private key temporarily with the user's password.
For each data item, randomly choose a (symmetric) key S and encrypt the data d with it. Store S(d).
Encrypt S with the the public key P+u of the user you want to grant access. Initially, that's the user u whose data you're storing.
Store P+u(S) permanently. Forget all other keys.
Now, when a user u wants to share the data with the user x, do the following:
Decrypt the user's private key P-u with the user's password.
Using that private key, decrypt the stored data: P-u(P+u(S)) = S.
Encrypt S with the public key of the user you want to share the information with.
Store the resulting P+x(S) permanently. Forget all other keys.
Now, when any user x wants to access the data, perform the following process:
Decrypt the user's private key P-x with the user's password.
Find P+x(S). (If it's not stored, that means nobody shared the data with the poor user x).
Using the private key, decrypt the stored data: P-x(P+x(S)) = S.
Using S, decrypt the stored encrypted S(d): S(S(d)) = d.