How to use MongoDB Client-Side Field Level Encryption (CSFLE) with Node.js
Rate this tutorial
Have you ever had to develop an application that stored sensitive data, like credit card numbers or social security numbers? This is a super common use case for databases, and it can be a pain to save this data is secure way. Luckily for us there are some incredible security features that come packaged with MongoDB. For example, you should know that with MongoDB, you can take advantage of:
The following diagram is a list of MongoDB security features offered and the potential security vulnerabilities that they address:
Client-side Field Level Encryption allows the engineers to specify the fields of a document that should be kept encrypted. Sensitive data is transparently encrypted/decrypted by the client and only communicated to and from the server in encrypted form. This mechanism keeps the specified data fields secure in encrypted form on both the server and the network. While all clients have access to the non-sensitive data fields, only appropriately-configured CSFLE clients are able to read and write the sensitive data fields.
In this post, we will design a Node.js client that could be used to safely store select fields as part of a medical application.
There are a few requirements that must be met prior to attempting to use Client-Side Field Level Encryption (CSFLE) with the Node.js driver.
libmongocrypt is required for , as it is the component that is responsible for performing the encryption or decryption of the data on the client with the MongoDB 4.2-compatible Node drivers. Now, there are currently a few solutions for installing the libmongocrypt library on macOS. However, the easiest is with . If you've got Homebrew installed, you can install libmongocrypt with the following command:
I ran into an issue with libmongocrypt when I tried to run my code, because libmongocrypt was trying to statically link against libmongocrypt instead of dynamically linking. I have submitted an issue to the team to fix this issue, but to fix it, I had to run:
By this point, all the appropriate components for client-side field level encryption should be installed or available. Make sure that you are running MongoDB enterprise on your client while using CSFLE, even if you are saving your data to Atlas.
Let's start by setting up all the files and dependencies we will need. In a new directory, create the following files, running the following command:
Be sure to initialize a new NPM project, since we will be using several NPM dependencies.
And let's just go ahead and install all the packages that we will be using now.
MongoDB Client-Side Field Level Encryption (CSFLE) uses an encryption strategy called envelope encryption in which keys used to encrypt/decrypt data (called data encryption keys) are encrypted with another key (called the master key). The following diagram shows how the master key is created and stored:
The Local Key Provider is not suitable for production.
The following script generates a 96-byte, locally-managed master key and saves it to a file called master-key.txt in the directory from which the script is executed, as well as saving it to our impromptu key management system in Atlas.
After saving this code, run the following to generate and save our keys.
And you should get this output in the terminal. Be sure to save this key, as we will be using it in our next step.
It's also a good idea to check in to make sure that this data has been saved correctly. Go to your clusters in Atlas, and navigate to your collections. You should see a new key saved in the encryption.__keyVault collection.
Your key should be shaped like this:
With the data key created, we're at a point in time where we need to figure out what fields should be encrypted in a document and what fields should be left as plain text. The easiest way to do this is with a schema map.
A schema map for encryption is extended JSON and can be added directly to the Go source code or loaded from an external file. From a maintenance perspective, loading from an external file is easier to maintain.
The following table illustrates the data model of the Medical Care Management System.
|Field type||Encryption Algorithm||BSON Type|
|Insurance: Policy Number||Deterministic||Int (embedded inside insurance object)|
|Insurance: Provider||Non-Encrypted||String (embedded inside insurance object)|
Let's add a function to our csfleHelper method in helper.js file so our application knows which fields need to be encrypted and decrypted.
Alright, so now we have the JSON Schema and encryption keys necessary to create a CSFLE-enabled MongoDB client. Let's recap how our client will work. Our CSFLE-enabled MongoDB client will query our encrypted data, and the mongocryptd process will be automatically started by default. mongocryptd handles the following responsibilities:
- Validates the encryption instructions defined in the JSON Schema and flags the referenced fields for encryption in read and write operations.
- Prevents unsupported operations from being executed on encrypted fields.
To create the CSFLE-enabled client, we need to instantiate a standard MongoDB client object with the additional automatic encryption settings with the following code snippet:
If the connection was successful, the client is returned.
We now have a CSFLE-enabled client and we can test that the client can perform queries that meet our security requirements.
The following diagram shows the steps taken by the client application and driver to perform a write of field-level encrypted data:
We need to write a function in our clients.js to create a new patient record with the following code snippet:
The following diagram shows the steps taken by the client application and driver to query and decrypt field-level encrypted data:
We can run queries on documents with encrypted fields using standard MongoDB driver methods. When a doctor performs a query in the Medical Care Management System to search for a patient by their SSN, the driver decrypts the patient's data before returning it:
If you attempt to query your data with a MongoDB that isn't configured with the correct key, this is what you will see:
And you should see your data written to your MongoDB Atlas database:
- Change directories to the Docker directory.
- Build Docker image with a tag name. Within this directory, execute:This will build a Docker image with a tag name mdb-csfle-example.
- Run the Docker image by executing:The command above will run a Docker image with tag mdb-csfle-example and provide it with csfle as its hostname.
We wanted to develop a system that securely stores sensitive medical records for patients. We also wanted strong data access and security guarantees that do not rely on individual users. After researching the available options, we determined that MongoDB Client-Side Field Level Encryption satisfies their requirements and decided to implement it in their application. To implement CSFLE, we did the following:
1. Created a Locally-Managed Master Encryption Key
A locally-managed master key allowed us to rapidly develop the client application without external dependencies and avoid accidentally leaking sensitive production credentials.
2. Generated an Encrypted Data Key with the Master Key
CSFLE uses envelope encryption, so we generated a data key that encrypts and decrypts each field and then encrypted the data key using a master key. This allows us to store the encrypted data key in MongoDB so that it is shared with all clients while preventing access to clients that don't have access to the master key.
3. Created a JSON Schema
CSFLE can automatically encrypt and decrypt fields based on a provided JSON Schema that specifies which fields to encrypt and how to encrypt them.
4. Tested and Validated Queries with the CSFLE Client
We tested their CSFLE implementation by inserting and querying documents with encrypted fields. We then validated that clients without CSFLE enabled could not read the encrypted data.
In this guide, we stored the master key in your local file system. Since your data encryption keys would be readable by anyone that gains direct access to your master key, we strongly recommend that you use a more secure storage location such as a Key Management System (KMS).
For more information on client-side field level encryption in MongoDB, check out the reference docs in the server manual: