Mongodb community - at rest data encryption in node js - node.js

I'm looking for a way to encrypt the entire DB and keep the ability to search for data although it's encrypted.
I have seen a lot of questions regarding encryption of at rest data in Mongo, but none of it got an answer that can help one complete a full flow for their application.
I hope to present here my findings and get feedback and more ideas (I still have some questions).
Encryption options:
1.mongoose-encryption.
Complete solution! Can encrypt all fo the db with minimal work for you!.
2. Procona mongodb - I didn't had a chance to test it, I've spent hours trying to install and get it to run, without luck (this is probably just me though..).
3. Create get and send methods to encrypt and decrypt your data in the Module level.
My requirements for at rest data encryption are:
Application layer does not need to be involved in the encryption- decryption process. Should be like we don't even have the data encrypted (for the most part).
We can perform search and lookups on encrypted data.
I don't know how to do that but hopefully search for partial words and phrases in encrypted text fields.
Of course that all data is encrypted expect for Object IDs.
My approach:
I want to try and use mongoose-encryption to use all the benefits of this amazing plugin.
I also want to add to the schema the Hash of the Real value in the encrypted field so I could preform find operations on encrypted field.
The problem:
I can't seem to find the correct mongoose Hook to temper with the non-encrypted data before mongoose-encryptions hides it. So I can't generate my Hash.
This doesn't work:
Users.pre('save', () => {
this.hashedName = hash(this.name)
console.log(":(")
});
Also as mentioned above, searching for partials and phrases in encrypted data.
With my approach we could find someone named "Danielle" but we can't search in Hash for users with a name that starts with "Dani".
Please give me your opinion as well for my approach. I know that this is a topic without easy to find solutions.

If you want to encrypt the data on disk, encrypt the entire disk and encrypt the swap. If someone gets a copy of the database (e.g. you forgot to put auth on the database and someone connects to the database and dumps the data) the plaintext is exposed.
If you want the database to store encrypted data only, use client side encryption. This requires key management on the client side but makes it so that someone dumping your database doesn't get the plaintext.

Related

Setting up a different encryption key for each record of a model in a Laravel 9 website

Introduction
For a website I'm working on, I will be storing confidential information that I need encrypted.
The way Laravel currently handles things, they encrypt each record with the same APP_KEY that's stored in the .env by default. I think I should be able to take that same APP_KEY and decrypt all the information in my production database as long as I have access to the production .env.
If so, I don't think that's a proper away to handle security for my case. Let's say I hire an admin for my production site and they turn out to be malicious. All they need to do to get access to all the users' sensitive details is take that APP_KEY and run the decryption.
If that's the case, I would rather have it work like this:
The user creates a record that has a "secret" field
A random string is generated to encrypt the data passed to the "secret" field and is then given to the user
If the user wants to access the data in the "secret" field, they have to use the key given to them; I nor anyone else should be able to decrypt that field without knowing the key given to the user
For my specific case, a client program will handle accessing the site's API and storing the encryption key and other details safely, so the user doesn't have to think about this.
My questions are the following:
Is the current encryption scheme Laravel uses safe from malicious admins?
If not, how would I go about best implementing the latter scheme?
Are there vulnerabilities with the latter schema as well, and if so, how can I improve it?
What I've Done
I've looked at the docs on this issue. I've also looked into the Model::encryptUsing feature, which allows for custom encryption.
I think I can implement the above if, when running the action that creates the record, I use Model::encryptUsing, but I haven't tested it yet.
If it ends up working, I will post the answer here.

How the encrypt/decrypt sails.js specific model values works?

I'm reading on Sails attribute documentation the encrypt and decrypt functions for attributes and tested it on a random field alongside with mongodb. This worked well and encrypted the field before saving it on database. So, according to documentation i can decrypt that data with decrypt method. This saves a lot of code validations and library importing. But, i was wondering if is possible:
To be able to verify without decrypt data (Like the compare function on bcrypt library)
To encrypt data on a production db, then change the project (update, replace models or something similar) and then be able decrypt that.
To be able to encrypt data, share the db with another sails project and be able to decrypt (or verify with something related to question 1) the encrypted data.
I just figured it out, seeking for related content on project.
I've found an object called dataEncryptionKeys in /config/models.js file, with the corresponding documentation reference. This answers questions as it is supposed to behave as the key (or keys) for decryption. For the answer of first i think that it will be ok with the decrypt method for most use cases.

How to store Encryption Keys while in use

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.

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).

Need suggestions on how to create a website with an encrypted database

I want to create a website where a user enters content (say a couple of sentences) which eventually gets stored in a backend database (maybe MySQL). But before the content leaves the client side, I want it to get encrypted using something on client like maybe javascript.
The data will travel over the web encrypted, but more importantly, will also be permanently stored in the backend database encrypted.
Is JavaScript appropriate to use for this? Would 256 bit encryption take too long?
Also, how do you query an encrypted database later on if you want to pull down the content that a user may have submitted over the past 2 months?
I'm looking for tips, suggestions and any pointers you guys may have in how to go about learning about and accomplishing this.
Thanks!
You shouldn't implement the encryption for the communication between the client and the server yourself, use SSL (https) for that. As for encrypting data in the database, you can always use MySQL's built-in methods, such as AES_ENCRYPT and AES_DECRYPT, see reference manual for details.
Look at http://www.farfarfar.com/scripts/encrypt/ (encrypt/decrypt).
Tried text/text/XXTEA with success.
However, that's about as far you can go with JS encryption.
As long as you're not using SSL/https, the main disadvantage is:
A fuzz to protect the private key, (it can be done though, like a form field, not submitted, but requires user to enter value on each session.)
About searching/extracting data on encypted data, I belive nothing is going to work.
Well, to point out something: if it's client side encrypted, it'll likely also be easily decrypted, seeing as with languages like javascript, they're being handed the source code of your encryption scheme. Plenty of encryption schemes out there, AES, Blowfish, etc, but if the data is traveling on an encrypted connection, I suppose the encryption of the data only adds very little security, maybe packet sniffers locally installed or something to that effect.
I would also suggestion you look into using compression as well, I myself have used LZMA, Huffman, and even base64 encoding with javascript to at least obscure the content from casual observers. Point being, no matter how good the encryption, you're handing over the process to the client, and they can just view your source and be able to easily reverse the encryption, given that they know the encryption scheme and aren't to lazy to do a bit of Googling or searching on Wikipedia. I personally prefer compression because it also reduces the size of the data being sent, and unless one is trained in analysis of encryption and compression, it is isn't easy to figure out the compression being used from sniffing packets.
EDIT: But if you want high levels of security, I would suggest against using just compression, and instead, using an asymmetric encryption method like RSA or Diffie-Hellman encryption on data traveling between the server and client, as the private key will never be shared.
For client side, javascript or better, a Java application, would work (at least you can't right click -> view source with a Java app in two seconds). 256 bit encryption wouldn't take too long if you're just wanting to save a few sentences like you said. A modern processor with blast through that in milliseconds. Then, when it arrives at the server, encrypt it using AES or another powerful algorithm (note that the US government uses AES 256 for their Top Secret documents) before sending it to the database. (Also want to add that you can also store a hash (MD5 is a function readily available in PHP, you can opt for something like Whirlpool as well, but you'll have to find a library for that) of the data on a separate database, and check against it when you access it to ensure that your database hasn't been compromised)
For querying, I suggest PHP. Read the data, decrypt it, then send to the client (in still encrypted form), or if you want, and have the client decrypt it also, if you don't trust the networks the data is being sent through.
Here's a good source on Javascript and AES: http://point-at-infinity.org/jsaes/
My two saved links using RSA in javascript: http://www.ohdave.com/rsa/ and http://www.hanewin.net/encrypt/rsa/rsa.htm
As for the data being sent over the network encrypted, this is the kind of thing that HTTPS was made for. Nothing is transmitted in the clear. It's encrypted securely enough for most banks to trust it. Don't reinvent the wheel here; it'll never be as round as what a bunch of people who make wheels for a living have come up with.
Have your form submit to an https:// url (rather than http://), and the script/app at that URL talk to the database, encrypting and decrypting the data as needed. Javascript won't easily talk directly to MySQL anyway, and if you do all the encryption client-side, anyone who can get the page can still decrypt it (since by necessity the client would need to be given the decryption key as well).
Check out this blogpost: http://www.ravellosystems.com/blog/all-you-need-to-know-to-configure-ssl-offloading/
It takes you through all the steps you need to do to configure your webs server to serve content under an encrypted channel (a.k.a SSL termination).

Resources