I have encrypted data in AES-GCM with the crypto API. The initialisation vector is then added to the data, forming a unique encrypted string stored in local-storage. Finally, the secret key is stored in IndexedDB.
Since the secret key is non-extractable I though it was secure enough for most use-cases. To my understanding, an attacker would have to rob both the local storage and the indexed db, find the initialisation vector inside the data, convert it to a buffer array and then directly perform the decryption in the browser before sending the data back to his server. Indeed, it seems the non-extractable nature of the secret key means that he would not be able to send the raw secret key to his server and thus not being able to complete the decryption.
But I've been told I was very wrong, and that my strategy was actually barely more secure than letting all the data directly readable in local storage.
So, how could I improve this workflow? Is it really that insecure? Would it be possible to encrypt and decrypt the secret key thanks a unique password provided by the user? The password would be in a .env file and thus never exposed to an attacker. How would you do it?
Here is how the secret key is generated so far:
crypto.subtle.generateKey(
{ name: "AES-GCM", length: 256 },
false,
["encrypt", "decrypt"]
);
Thanks for your help!
Related
I am building a Node.js application that receives a long term access token from another application that I need to access. I don't want to store these access tokens directly in the database since anyone who has access to these tokens can basically do whatever they want with it it.
I am new to Cloud KMS and such systems in general but spent the last few hours learning it. It seems like the ideal solution to solve my problem but I'm not entirely sure what strategy I should follow to store these access tokens:
Should I store an encryption key in Cloud KMS and use that encryption key with an NPM package like this one to store access tokens in my database?
Should I store the access tokens in KMS directly? My assumption is that I would have a key store and keys rotated every 14 days. Whenever I receive an access token, I simply encrypt it and store it at KMS. I only store the ciphertext in my database. When I need to access the access token from KMS, I use the ciphertext to decrypt it.
Which of the above is the right way to use KMS? If it's option 2, I have additional questions:
Can I encrypt a large number of access tokens with a single key or do I need to create a new key for every access token?
If I ever need to modify the access token encrypted at KMS, can I simply modify it or do I need to destroy the old version and encrypt again?
Thanks for your help!
I think your best option is to use the Node.js API provided by Google to encrypt the tokens and store the resulting ciphertext in your database.
When the application receives a token from the other application it encrypts it with the API and compares to what it has in the database to see if it's valid, this way the plain text token is only known by the owner.
Can I encrypt a large number of access tokens with a single key or do I need to create a new key for every access token?
You can encrypt as many tokens as you want with the same key. Creating a key for each token would become unmanageable pretty soon, and unless they key itself it's compromised (which is hard to imagine being stored only at Google) there is no significant risk.
If I ever need to modify the access token encrypted at KMS, can I simply modify it or do I need to destroy the old version and encrypt again?
KMS is not storing your data, either encrypted or in plain text, it's just storing the KEY you need to either encrypt or unencrypt your data.
Following the method of storing only the encrypted version of the tokens, when you need to modify one token, it should go like this:
Client sends you the token that needs to be revoked.
Your application encrypts it and compares it with the tokens stored in the DB
The new token is generated (by your client application I understand?)
It's sent to your application, which encrypts it
The old version of the token is substituted with the new version
The client now can use the new token as it has the same validity as the previous. If it tries to use the old token, as it's not in the DB anymore it won't work.
Regarding key rotation, when it happens the new tokens will be encrypted with the new keys. The old tokens will still be unencryptable because your old keys are still on KMS, just not being used for encrypting anymore. However, if you destroy the key they were encrypted with, then they will be unrecoverable.
As of December 2019, the preferred way to store and manage secrets on Google Cloud is Secret Manager:
$ echo -n "my-access-token" | gcloud beta secrets create "access-token" \
--data-file=- \
--replication-policy "automatic"
You can then access secrets from your applications. Here is an example with Node:
function getSecret() {
const [version] = await client.accessSecretVersion({
name:"projects/<YOUR-PROJECT-ID>/secrets/access-token/versions/1",
});
const auth = version.payload.data.toString('utf-8');
// auth is "my-access-token"
return auth
}
Any services that need to access the secret need roles/secretmanager.secretAccessor permissions on the secret.
Your option 2 is fine, as long as the access tokens are small enough to encrypt with the API (a few thousand bytes or smaller). You can encrypt as many tokens as you need with the same key without impairing security.
14 day key rotation seems more frequent than necessary unless you have a specific need for it.
I don't understand your modification question. If you modify the access token and wish to save the modified version, you should probably encrypt it with your key then save the encrypted data.
I am creating an application where I need to store client's information(Like their API Keys and API Secret to access my service, along with other confidential information).
Now, in the database, I want to store these in the encrypted format. In this regard, I decided to with symmetric key cryptography, AES in specific to encrypt the details.
However, for security purposes I want to use a different AES encryption key on a per client basis, so that even if the DB is compromised, all the data cannot be decrypted using a single key.
However, due to obvious reasons, I do not want to store my private keys in the DB with the encrypted informations.
So, I cannot seem to decide how to store my keys, especially since I need to have a binding that which key belongs to which client.
How can I achieve this, and which is the best approach in scenarios like this?
Use a KDF to derive an encryption key from the users password and then use this key to encrypt their private information.
When any action is to be taken that requires their API secret or whatever other private data you are storing, simply request the users password and use it to derive the key again and use the key to decrypt.
If you want users to be able to change their password, add an intermediary random key for each user and use this key to encrypt their data. Use the key derived from their password to encrypt the random key. Then when changing the users password, you only need to decrypt and re-encrypt the random key.
I want to do client side encryption in mobile app and the encrypted data will be saved in server. The data should be decrypt when user logged in another device also.
Is there any other way to store the "key" securely, which is used for encrypt and decrypt my data other than my server? Usually we store encryption "key" in server, but i need more secure place than my server?
Can we use solutions like AWS KMS for this?
You could just never store the (presumably symmetric) key on the device, but instead deterministically regenerate the key each session using a Password-Based Key Derivation Function
I've a website that users submit their personal data to, and I'm thinking of encrypting these data using AES-256 and their password is used as a key for that encryption and then I store the encrypted data in a MySQL database...
Now if the user changes his/her password, how would I change the key of the encrypted data?
Should I gather all the data from the database, decrypt their data with the old key, and then encrypting it again with a new key?
You don't need to re-encrypt all of the user's data when they change their password.
Generate a secret key to encrypt a user's data; call this the "content encryption key." Derive a key from the user's password; call this the "key encryption key." Encrypt the "content encryption key" using the "key encryption key." Store the encrypted key along with the salt and the number of iterations used for key derivation.
If they change their password, decrypt the content encryption key with the old password, and re-encrypt it with a key derived from the new password. You should choose a new salt for the new password, and make sure you store it along with the new encrypted key.
Because the content encryption key is randomly chosen from a huge space, you can safely use ECB as the cipher mode when encrypting it.
Don't simply hash the password, even if you use salt or even if you use an as-yet-unbroken algorithm. You need to repeat the hashing operation thousands of times. There are libraries for doing this (correctly) on most platforms. Use a key derivation algorithm (PBKDF2, from PKCS #5) to create a secret key from a password.
This concept follows the draft for password-based S/MIME encryption.
One possibility to consider decouples the key used to encrypt the data from the key used to gain access to the data. Done carefully, this allows the user to change their password as often as they desire, while you only change one record in the database. Separately, you can schedule changes to the key(s) encrypting their data when it is convenient for you.
How does it work?
You encrypt the data D for user U with a randomly generated key, KU,D.
You encrypt the key KU,D with a separate key K1U,K generated from a random salt, S1U (which you keep a record of) and the user's password P1U (which you may or may not keep track of). The encrypted key is E1U.
You store S1U and K1U,K ready for when the user wants to access their data.
When user U wants to access their data, they provide you with their password, P1U, and you look up S1U and regenerate K1U,K from that data, and use that to decrypt E1U, giving you KU,D once more, with which you decrypt their actual data.
You ensure you can detect when the password given is correct so you don't spew forth binary gibberish if the users types the wrong password.
The advantage of this level of indirection comes when the user wants to change their password. If you don't use some technique analogous to this, you will have to get and validate the old password and the new password, decrypt all the data with the old password, and re-encrypt it all with the new password.
With the level of indirection, you still prompt the user for their old password (P1U) and their new password (P2U) and validate them, but you only have to decrypt E1U and then re-encrypt it with a new key K2U,K generated from a new salt S2U and the new password P2U. You do not have to touch the encrypted data at all.
With the level of indirection, the system S can also keep a second encrypted copy of the data key KU,D, encrypted with the system's password. If it becomes necessary or desirable to change the key used for encrypting the data, the system can use its encrypted copy of the key to do so. It can keep a record of which key was last recorded by the user in their key, so when the user returns to look at the data, it can arrange to to change the stored key K2U,D because at that time, it has their password (the rest of the time, it does not).
This is a mild variation on some of the ideas in "Cryptography in the Database: The Last Line of Defense" by Kevin Kenan. The KnU,K keys are examples of a KEK, a Key-Encrypting Key. You could also read about key families in the book, which would help with the management of encrypted data.
First, you generally shouldn't use the password as an AES key. Maybe something like a cryptographic hash (not MD5) of the password + a salt (you would store the salt but not the hash in this case).
One thing you could do is encrypt each user's files with a random key, then encrypt that key with the hashed+salted password. If the user changes passwords, you only have to re-encrypt the key.
That’s silly.
AES uses a 256-bit key, so when you say that you will be using their password for the key, it won’t be nearly as long as the key size requirement.
I'm implementing the provider side of a two-legged OAuth protocol for API authentication. We will provide the consumer with a consumer key and secret, which they will use to sign requests. The 2-legged OAuth is dictated by an interoperability standard, and thus a requirement.
The secret is sort of akin to a password, and I would never normally store a password as plain text (bCrypt or similar would be my normal choice). But because my provider needs access to the plain-text secret to verify the signature, it has to be either in some plain-text or reversible form.
I've considered the following options:
Store the secret as plain text
It's the most obvious solution, but if the database is compromised somehow, then all of the secrets will have to be changed. To me this solution is not ideal because it has all of the problems of storing a password in plain-text.
Apply reversible encryption (e.g. AES) with an encryption key stored elsewhere
This will provide some security, because if the database is compromised then the secrets will still be safe. But reversible encryption requires an encryption key, and the key has to be stored on the server. It means that if an attacker compromises the machine, then the encryption can be circumvented.
Is there something I haven't thought of?
Clarification Effectively it's using 2-legged Oauth as a single-signon system. The 'consumer' creates a request including the consumer key, a nonce, and several other parameters. The whole request is then signed by computing an HMAC-SHA1 with the consumer secret. When the request reaches our provider system, the process is repeated and if the signatures match then the request processing continues. We therefore need the plain-text secret to compute the HMAC-SHA1 signature on our side too. Unfortunately this mechanism is dictated by the industry-standard protocol that we need to comply with.
Take a look at this previous question. I'm not an expert on the topic, but I think you're missing part of the equation. In addition to the consumer key and secret, you'll be verifying the application that's sending the request (using an x509 certificate if you're using RSA-SHA1).
Are you sure that the provider needs the plain text password?
If this is the case then you simply can't have a 'customer secret'. As soon as the customer discloses this secret to someone else (including you) it fails to become a secret any longer.
Maybe if you explained more of what you are trying to do we could come up with a more elegant appraoch.