Encrypt sensitive data when sending to the server

Let’s say I have some sensitive data, e. g. an API key users have to add. I now want to send this key to the server and prevent any man-in-the-middle attacks. I’m using HTTPS for the communication, but I’m not sure if this is “enough” in such a scenario.

Would you recommend adding another encryption layer that is only known to the client and the server, and if so, what is the best way to implement this? The server should still be able to decrypt the key when needed.

Passwords and login tokens are sensitive data being exchanged between client and server in a regular meteor app

Yes, I know. But IIRC, passwords are already encrypted on client-side, right?

If you are concerned about mitm attacks, you can also not trust the client app itself that is being sent through the same channels

@waldgeist

1) Mongo Auto Field Encryption Package

2) Environment variables

API keys and other passwords are usually set as environment variables, that are only serverside. For example, here is adding some new environment variables at various platforms:

If there is some need to copy some environment variable to clientside, it’s possible by setting public variable:

Or if that environment variable is only needed serverside, it can stay at environment varible, or copied to private variable like Meteor.settings.headerLoginId .

API keys are usually needed to exist unencrypted at serverside.

3) User accounts

For user accounts, Meteor accounts packages save password in hashed format to database.

Before showing any sensitive data, it’s useful to check is user logged in:

if (Meteor.user()) {
...
}

4) Admin Panel

If at Admin Panel UI it’s needed to save some new password to database, it’s useful to just save it, for example with Meteor Method, but not load it back, not adding it to PubSub minimongo content.

2 Likes

Encrypt

import CryptoJS from 'crypto-js'. // this library is now deprectated but ... "Nowadays, NodeJS and modern browsers have a native Crypto module. " - Check the NPM details.

const passphrase = process.env.PHRASE // or your safe way to store a pass. This can also be a userId, session id, anything that you can pick up on the server for the same user. The userId can also be altered at both ends so that you cannot reverse engineer it.
return CryptoJS.AES.encrypt(string, passphrase).toString()

Decrypt

import CryptoJS from 'crypto-js'

const passphrase = '' // same as above
const bytes = CryptoJS.AES.decrypt(string, passphrase)

return bytes.toString(CryptoJS.enc.Utf8)

1 Like

You can generate a key pair: Public key + Private key.
Send the Private Public key to client, client will use this key to encrypt data then send to server.
On the server side, use private key to decrypt the data.

// Edit: Send the public key to the client, not the private one.

1 Like

Thanks. What is a good library to use for encryption on client and decryption on server side?

Do you happen to know how Meteor does it when sending the password to the server on sign up?

I saw that Accounts.createUser() also exists on the client. Unfortunately, it automatically logs the user in on completion. In my current use case, I want to create an admin interface that allows you to create new users.

I believe Meteor doesn’t send raw/plain password to server. It send sha256 hash instead. So in worst case, some how they intercepted your connection, decoded ssl/https encryption and got this hash, they can use that hash to login.

I use node-forge npm package to generate key-pair, encrypt and decrypt. It’s big package though.

1 Like

Thanks. I had a look at the code of meteoreact:accounts and learned that you can actually send a hashed password as an object with the properties digest and algorithm using Accounts._hashPassword().

Fun fact: the TypeScript type annotations of Accounts.createUserAsync() do not know about this “password object” syntax, although the server does. So I am not sure if this is a somewhat “undocumented feature”, too.