I would like to protect my database of secret information with a master key or master password and encrypt the data. Only if the user enters the correct master key, the data will be decrypted.
Obviously, when creating the master key, I should only save this as a hash value (e.g. SHA). But then I also need a key to encrypt the data with (e.g. AES). I thought of using the master key's hash value as the key for encryption.
But probably, this is not safe, right?
If the user enters a key, the hash is calculated and compared to the saved hash value. If they are the same, the database should be enrypted.
But saving the master key's hash value and using it as the key for encryption is probably a security risk, right?
Should I rather use the actual (plaintext) version of the master key to encrypt the data with?
Or just leaving out the step with comparing the hash value to the entered password and instead just trying to encrypt the data with the password entered?
I hope you understand what I'm trying to tell you about my problem. Thanks a lot in advance!
It is always best to separate responsibilities clearly and only use one cryptographic entity for one purpose and nothing else.
For symmetric encryption (e.g. AES), you need a key, and such a key is typically derived from a password (but it can be derived from a lot of other things, like a collection of files, or even just entered directly). So this entity is "password-which-becomes-encryption-key". Use it for that purpose. No need to store the password anywhere, as deriving the correct key from it is all you need.
If you additionally want to guard access to your application or database with an account system with authentication and authorisation, you also need to manage those credentials. That's an entirely unrelated activity; look up any basic web application design guide for standard solutions.
Just don't reuse a login password as an encryption key.
Related
I have a database and an API in NodeJS, I create users with web app, and each user can create/update/delete data.
To secure this a bit, I need to encrypt data of users. So what I want is creating a pair of SHA256 public private keys each time a user is created.
Actually what I do is storing thoses keys in database, by encrypting them with global SHA256 pair of key.
So, in a nutshell, I have a global pair of key to encrypt each specific pair of keys for each user.
The fact is that seems to be not really secure because finally each user have his own encryption/decryption method stored in the database.
For example I can have 2 tables :
User table :
id_user | firstname | lastname | encrypted_data
-----------------------------------------------
1 | John | Doe | QMwmuCMmI..
2 | Jane | Doe | QMwmuCMmI..
...
Keys table :
id_user | public | private
------------------------------
1 | MIICIjA.. | MIIJrT..
2 | MIICIjA.. | MIIJrT..
...
So the link from John Doe to his public and private keys in simple.
A problem is that I can't ask for user to create a pair of private/public key and send me only public, because all need to be automatic, user don't have to do anything.
Another problem is that the application should be usable on any device, so the private key can't be stored in client side.
You can't just store them on the server as the attacker is on the server. The malicious hacker can easily find the keys (as they are un-encrypted), download the entire database and decrypt everything.
So, your best options are storing the keys on a completely different machine and let that machine do the encryptions and decryptions. That way the attacker would first have to break into your machine, and then the extra step of into the encryption/decryption machine. Not impossible, but hopefully much harder.
Basically, nothing is un-hackable. Making something as difficult as it can be for the attacker is the way to go.
Also, even if you strictly have to store the data in a database that your main server has access to, do NOT store them in the same table (as shown in your example how you kept them in separate tables). Keep the indexes of all data added to the user database the same, so that index 1 of users database will result in the correct key for that index (exactly what you did).
Another option would be cryptographic hardware. I'm not familiar with this, but I do know that some companies sell hardware that perform all of your cryptographic needs.
Disclaimer: This answer exposes two solutions to secure user data, but exclude SHA256 public/private keys from the equation (for, I believe, something more secure). It might not be an acceptable solution.
The "easy" way
I believe it's how Termius does it. In this case you would use the password to secure both the account access and encrypt data.
You would end up with something like that in database:
id
email/username
password_hash (to secure the account access)
... as many fields as you want, values are encrypted using a symmetric algorithm (i.e. AES) using the unhashed password
Advantage:
(optional) end-to-end encryption
instead of doing the decryption server-side, send the encrypted data as-is to the client. It will be the client app responsibility to do the decryption before the visualization, and to encrypt everything before sending it back to the server.
Drawbacks:
potential data loss
if an user loose its password, he also looses access to all its data.
the encryption key is stored in the database
sure, it's hashed. If the hashing algorithm is strong enough, it could be enough security for quite some time. But for some 3 letters agencies, it might be reversable relatively fast 🤷♂️
Warnings:
don't use the password hash as the key, only the unhashed password. Otherwise, the encryption key is stored "as-is" in your database (the password_hash field).
always use a salt when you compute passwords hashes (might sound obvious to say this in 2021, but still, I prefer to say it)
Even more robust encryption
I believe this is the way ProtonMail encrypts its users' data.
When you create a Protonmail account, you need to provide two passwords: the first one is used for account login, the second one for data encryption.
Advantages:
you don't need to store the second password in your database at all (hashed or not).
If the data can't be decrypted, it means the second password (aka the encryption key) is incorrect, its as simple as that.
(optional) end-to-end encryption
same as described in "the easy way".
Drawback:
potential data loss
if an user looses its second password, he also looses access to all its data. If he looses its first password, then he just looses access to "authentication". The typical "I forgot my password" flow works here, without raising any issue related to the encrypted data.
Measuring the pros and cons
Both solutions have a main drawback: potential data loss.
If you go for the Protonmail-like solution, I would say this drawback is compensated by a major advantage: the user is the only actor of its data's security, you store no hints about the encryption key in your database. Therefore, if you use industry-standard encryption algorithms you aren't responsible at al. Hackers would have no solution other than brute-forcing the encryption key of every user, one by one.
Plus, if you educate your users about the consequences of loosing their password, you can also claim that you are 100% unable to access their data at the same time. This is the highest possible level of data privacy, and this is what the world needs IMHO (no matter what type of data we are talking about).
I am making a social media type website, and I store user details such as emails, names and other personal details.
I will be encrypting the personal details using an Encrypt-then-MAC concept. When the user registers, a cryptographically secure string will be made to use as the private encryption key. When the user selects a password, the encryption key will be encrypted using the password.
The password will NOT be stored in the database, but will be the private key to decrypt the encryption key used to encrypt the personal details. The only person who knows the password is the user. My question is: how can I store the encryption key once decrypted?
I have thought of having a table with one column for IP and another column for the encryption key, but some people close the browser window without logging out, therefore there would not a possible way to remove the entry from the database when they have finished their session on the website.
Another way would be to store it in a cookie, but that could be intercepted when sent back to the server. I would like to know if there is a secure, nearly foolproof way to store the encryption key, client side or server side.
Thanks in advance.
EDIT:
In reply to TheGreatContini's answer -
The idea of a "zero-knowledge web application" (in your blog) is a good one, however, for zero-knowledge, even the key cannot be stored in the database, this complicates things a bit, as you would then have to use the user's password as the key. Using the password isn't as secure, as it is a bit harder to verify the password to prevent data which has been "decypted with the wrong key" from passing. There is the concept of Encrypt-then-MAC but that only verifies if the data is legit, and will assume that a hacker has messed with some data and data cannot be trusted, however, as you cannot actually verify the password (the hash would not be stored as it is "zero-knowledge"), so the password may just be wrong.
Not sure I have the answer, but a few considerations:
(1) Sessions need to be timed out. Perhaps you can do this by periodically running batch jobs that scan the database looking for sessions that have lacked activity. This requires storing in the db the date of the last action from the user.
(2) Generally keys are higher value than the content they protect because the keys have a longer lifetime than the individual data elements that the protect (because the data may change or additional data may be added). Rather than storing the key in the db, you can store the decrypted contents in the database for the length of the session. Of course, this is provided that you did (1).
Perhaps I am not adding much beyond what you already know, however may be worth considering a blog I wrote exactly about this topic. The low level details start in the section "A second line of defence for all the sensitive data." Prior to that it mainly motivates the concept. Glad to see somebody actually doing something like this.
I am trying to build an application that stores user related information client side in localstorage. I am encrypting that data with a password given by user.
If I implement forgot password and generate a new password how can I get back my data that is encrypted on old password.
I am using sjcl for encrypting data. Is there any technique to encrypt data with 2 passwords??
What would be an ideal pattern for this scenario??
The conventional approach for this is called "key escrow." Basically, it means giving a copy of the key to someone that you trust.
If you won't trust anyone, then key escrow is not for you. Your only option is to make sure that you don't lose the one-and-only key. And this is a fairly common approach too. Many products that advertise secure storage emphasize this point. As examples, see Bruce Schneier's password manager "PasswordSafe," and LaCie's security-focused DropBox alternative, "Wuala."
There are accepted methods for encrypting data so that it could be decrypted with any one of several passwords. But I don't see how this helps; if you can't remember one password, how will you remember two?
Any other approach that pretends to avoid key escrow but still provides a backdoor to access your data if you lose the key is not secure and no one should trust it.
In one of my applications, I am to store user credentials and tokens. As the credentials are used directly on third-party services, I cannot hash them and thus need to store them as-is.
As I am not an expert on encryption, I googled and found that AES 256-bit key size-is a good idea to encrypt such data.
I would like to know the opinion of the SO community on the same, before I make a final decision.
Thanks!
Edit: Thanks to all for discussion, I am moving ahead using AES256 as the encryption mechanism for now, which seems like a good choice.
if you ask user for credential every time, then why do you need to store them in db? just keep it in memory and pass to external system. you can even ask user once and keep their password in memory for the whole session. if, for some reason you have to store them in db, them of course encrypt it. as far as i know, current standard is AES256. but still somewhere you have to keep unencrypted key.
to sum up: if you want to authenticate users and then use their password only for the time of session then you don't have to store it in database. keep salted hash for authentication purpose and keep user provided password in session for external systems
btw. is your swap encrypted?
I was thinking about creating a Web app that would let people enter text (using SSL connection) and it would be encrypted before saving to the DB. The goal would be to have it so that only users could decrypt it.
You could have the users enter the key along with their data, and enter it again when they want to see the data, and not store the key. That would be kind of painful for the user, though.
However, if you stored the key on the server you'd have access to it and could potentially decrypt their data.
I don't think it's possible to do it without either having the user enter the key every time or storing the key, but is there some way that I'm not thinking of? Like maybe generating a key from information only the user knows? Something involving cookies?
You should look into public key cryptography. The basic idea is that you can encrypt information using a public key that only the holder of the private key can decrypt. In your scenario, the server would have a record of all the users' public keys and use them to encrypt the information. Then your users would use their private keys, which the server never sees, to decrypt the data.
If you're looking for a way to store the private key client-side, you could look into PersistJS.
Sounds like you could do something using PGP. As a previous post mentioned you would have a public and private key. The private key can be secured by a passphrase. That way you could have the private key potentially stored on the db, since it would still require a passphrase to use it.
The huge problem is that if the user should forget that passphrase, they could lose that data. You could get around that by using an Alternate Decryption Key (ADK). That key is automatically encrypted with everything and can be split between multiple individuals.
From an information security perspective, this only makes sense if the encryption/decryption is done on the user's computer, and not your server (since there's no guarantee that you're not storing the key and/or the plaintext). JavaScript is out of the question, so you need a client application*.
Also, public-key cryptography is computationally expensive. You might want to keep that in mind if you have a lot of users and decide to do encryption/decryption on the server.
* or a Java applet, but that's so 90's. Silverlight or Flash could potentially work, too.