Serverless: Handle API key decryption in a lambda function - python-3.x

I am implementing an API that uses a third party library.
The third party library provides a key which needs to be passed in as an input. The key is dynamic and can change based on consumer/business scenario. The lambda function should be able to decrypt the key.
Can someone suggest a way to decrypt a key? I am exploring aws-kms approach on the side.
Please note: i have noted down the .env way of achieving it. But, today my API is being consumed by one consumer hence one API key. Tomorrow, the number will increase (would result into multiple keys) and i may not be in place to store/update the function.
Edit: I need to pass some sensitive information through payload. This can be an alphanumeric value. e.g.
{"sender": "+123", "secret": "encrypted_value"}
The client and server should share a key using which client can encrypt the info and server (lambda function) should decrypt it.
Any suggestion would be great! Thanks!

The standard way of doing something like you described on your "edit" section using KMS is:
Client calls KMS directly to generate a data key. Client will get back a key in its encrypted and plain format.
Client encrypts the data with the plain key, throws it away and send encrypted data and encrypted key to the server.
Server calls KMS decrypt operation, gets back the plain key and uses it to decrypt the data. Server throws away the decrypted key and uses the decrypted data as it wishes.
Please let me know if you meant something different, but this is a fairly standard way to use KMS. Of course, you need to lock down all of the APIs using IAM and KMS policies as your use cases determine.

Related

Using AES GCM without authentication tag in Node.js?

I'm using node:crypto API, namely createCipheriv() and createDecipheriv() with aes-256-gcm cipher to encode/decode a stream of data. However, it looks like I need to call decipher.setAuthTag() in order to decode the stream correctly, otherwise it throws an authentication error in the end (however the data is decoded correctly).
Is there a way to avoid using the authentication checks with this cipher? I'm using streams of data and it's very inconvenient to store auth tag with the data (I'm using multiple storage options, one of which is a plain filesystem). The data consistency can be checked by other means.
Or maybe you could recommend a universal auth tag storage option that I could use with streams (which doesn't require random access and rewind)?
You need to use the auth tag if you are using GCM. GCM is CTR with authentication built in, you could look into using a CTR cipher Node Forge has this option.
Alternatively, you could append your tag to your ciphertext and store everything together? Not sure what the details of your storage system are that make that inconvenient.

Generate a sufficient secret for JWT NodeJS Lambda

I've been looking at implementing JWT for the first time using jsonwebtoken (https://github.com/auth0/node-jsonwebtoken). For that, I need a secret value.
Is there a recommended command, or site, to generate a sufficiently good one?
I found this page (https://security.stackexchange.com/questions/95972/what-are-requirements-for-hmac-secret-key) which goes into detail about how long a secret should be (the answer seems to be a 256-bit), but where do you get one from? :)
Else it seems the other option would be to use a public/private key pair. They seem to prefer that approach on this guide I found: https://medium.com/#siddharthac6/json-web-token-jwt-the-right-way-of-implementing-with-node-js-65b8915d550e since that guy says he started off using a string and then switched to using a key pair. However the complication is this will be running on Lambda so I would ideally like the secrets (string or key) to be in environment variables. Not kept as files. But if you put a certificate in an environment variable, I wonder if AWS will strip out newlines and so screw it up when Node tries to work with it. So I'm thinking a secret string would be simpler - as long as it is sufficiently strong.
Thanks!
This is what I did when implementing HapiJS with JWT2. I generated a key based on the documentation they provided. According to their repo, this is one of the simplest ways to generate a secure key to sign against for JWT.
node -e "console.log(require('crypto').randomBytes(256).toString('base64'));"
I don't think you have to use asymmetric key authentication with public/private keys for JWT. In simplest forms, when a user logs into your system, they are given a hash of user data. On the client side, you provide that hash in the authorization header with each request. The server will check the hash to verify integrity. Since you have the key that you hashed against, it's highly unlikely that they will be able to create a forged hash.
Check out this link to the GitHub issue where they discuss generating keys for Hapi-auth-JWT2.

How would I achieve encryption/decryption for users, while also having the ability to decrypt everything serverside

I'm not entirely sure if I should be encrypting data server side or client side to begin with. The data is a json object of some user input. What I need to be able to do:
a) encrypt data at rest [in dynamoDB]
b) decrypt data and send it back to the user when they request it
c) decrypt the entire database to run analytics and ML
First Approach
1)Send the newly inputted user data through HTTPS, then encrypt it server side before it hits the database (in a lambda function). Now the data is encrypted at rest.
2) When a user makes a GET request, have a lambda function that decrypts it before returning it to the user
3) simply run a decryption on the database with the same technique
I'm not sure if this is good or bad, what type of encryption to use, etc.
EDIT: I will be doing server side encryption. If anyone has any suggestions/guides on a simple but good way to do it, perhaps using some npm package, that would be great.
Question 1: Encrypt server side or client side?
The only advantage of client-side encryption would be performance: remove the burden of your server from doing this work, pass it off to the end user machine.
However, there is a very big disadvantage: security. If you are encrypting everybody's data with the same key, then now everybody has access to that key, because it needs to be delivered to the client for decryption. You have entirely defeated the purpose of the security, because the cryptographic key now lives everywhere.
What about encrypting every user's data with different keys? That's a can-of-worms question, because you need to then consider where you are storing all the cryptographic keys. If it is in the same database as the data, you have again defeated the purpose of encryption -- putting the keys and data together is a no-no. You can come up with arbitrary solutions around this, but I assure you there are many considerations and you are inviting a lot of complexity.
Question 2: Is your approach reasonable?
Yes it is, but you need to understand what you are protecting against. Encryption at rest mainly protects a database (including backups) provided that the encryption key never gets put in the same place as the database. Managing the encryption key is something that needs to be carefully considered. There is good guidance on that if you care to research it, but it really is the task of a security architect to design this right.
You can use AES encryption using cryptojs.
Hit the server to get key for using in encryption process.
let AES = CryptoJS.AES()
// AES encryption
let encryptedUsername:String! = AES.encrypt(secretMessage: (username_tf.text)!, secretKey: REQUESTKEY!)
let encryptedPassword:String! = AES.encrypt(secretMessage: (password_tf.text)!, secretKey: REQUESTKEY!)

is client based online encryption practical?

I'm wondering whether a mechanism exists that allows client to client encryption. For example, when enabled, any information that is entered on one client can only be decrypted using a specific key.
Similar to how regular public key transactions work, but server agnostic.
A use case:
Everything on my Facebook profile is encrypted, and no body would be able to view that information (not even facebook). The users that I give the key would be able to decrypt that information.
This would allow complete control of data stored online.
The same idea can be applied for pictures uploaded to the internet.
One issue that I see is to have a practical mechanism to manage keys and a secure way to distribute keys to other users.
Has anyone done something like this before?
In case of Facebook I can imagine encrypting the data with OpenPGP keys into armored (text) format. Then you can place encrypted block to facebook or anywhere else. Other users would take the block, decrypt it on the client side and see it.
The same applies with other social networks and places where you can store some text block.
You can easily do encryption in some client application and even in Javascript (if you manage to make JavaScript load local user's keys somehow).

Encrypting text

I'm wondering if it's possible to encrypt text client side.
The situation is that I have a block of text I want to encrypt, and then save the encrypted text to a file. Only after a certain amount of time has passed will my program decrypt it. Another requirement I have is to be able to transfer the file with the encrypted text and be able to decrypt it with other copies of my program on various machines.
The problem is where to store the key. The only implementation I can think of that would work is if I store the key server side, and have it return the key after a certain time has passed.
Is there a way to do this client side (i.e. without any internet access)?
You can surely encrypt text on the client side, but the real problem is key management.
If the assumption is no Internet access, then you're going to have to package a certificate in the client that contains both keys, private to encrypt, public to decrypt, unless you use a symmetric key. Was going to suggest just generating a local, temporary symmetric key, but you state you must be able to decrypt the text on other machines that won't have it, so that won't work. At first blush, and maybe I'm overlooking something obvious, is packaging a cert on the client with public and private keys, and that introduces its own set of problems, eg protecting the cert with the private key.

Resources