Automatic Client-Side Field Level Encryption
On this page
- Installation
- Install
libmongocrypt
- Install the automatic encryption shared library (Ruby driver 2.19+)
- Install the
mongocryptd
(Ruby driver 2.18 or older) - Add
ffi
to your Gemfile - Create a Customer Master Key
- Configure Clients
- Create a Data Encryption Key
- Configure Encryption Schema
- Known Limitations
- Working with Data
- Encryption Key Management
- Customer Master Keys
- Data Encryption Keys
- Encryption Keys Rotation
- Adding Automatic Encryption To Existing Project
Since version 4.2 MongoDB supports Client-Side Field Level Encryption (CSFLE). This is a feature that enables you to encrypt data in your application before you send it over the network to MongoDB. With CSFLE enabled, no MongoDB product has access to your data in an unencrypted form.
You can set up CSFLE using the following mechanisms:
Automatic Encryption: Enables you to perform encrypted read and write operations without you having to write code to specify how to encrypt fields.
Explicit Encryption: Enables you to perform encrypted read and write operations through your MongoDB driver's encryption library. You must specify the logic for encryption with this library throughout your application.
Starting with version 9.0, Mongoid supports CSFLE's Automatic Encryption feature. This tutorial walks you through the process of setting up and using CSFLE in Mongoid.
Note
This tutorial does not cover all CSLFE features. You can find more information about MongoDB CSFLE in the server documentation.
Note
If you want to use explicit FLE, please follow the Ruby driver documentation.
Installation
You can find the detailed description of how to install the necessary dependencies in the driver documentation.
Note the version of the Ruby driver being used in your application and select the appropriate steps below.
Install libmongocrypt
This can be done one of two ways.
Add the libmongocrypt-helper gem to your
Gemfile
orDownload the
libmongocrypt
release archive, extract the version that matches your operating system, and set theLIBMONGOCRYPT_PATH
environment variable accordingly.
Install the automatic encryption shared library (Ruby driver 2.19+)
If you use the Ruby driver version 2.19 and above, the automatic encryption shared library should be installed by following the instructions on the Automatic Encryption Shared Library for Queryable Encryption page in the Server manual.
The steps required are as follows:
Navigate to the MongoDB Download Center
From the Version dropdown, select
x.y.z (current)
(the latest current version).In the Platform dropdown, select your platform.
In the Package dropdown, select
crypt_shared
.Click Download.
Once extracted, ensure the full path to the library is configured within your
mongoid.yml
as follows:
development: clients: default: options: auto_encryption_options: extra_options: crypt_shared_lib_path: '/path/to/mongo_crypt_v1.so'
Install the mongocryptd
(Ruby driver 2.18 or older)
If you are using an older version of the Ruby driver mongocryptd
will
need to be installed manually. mongocryptd
comes pre-packaged with
enterprise builds of the MongoDB server (versions 4.2 and newer).
For installation instructions, see the MongoDB manual.
Add ffi
to your Gemfile
The MongoDB Ruby driver uses the ffi gem to call
functions from libmongocrypt
. As this gem is not a dependency of
the driver, it will need to be manually added to your Gemfile
:
gem 'ffi'
Create a Customer Master Key
A Customer Master Key (CMK) is used to encrypt Data Encryption Keys. The easiest way is to use a locally stored 96-bytes key. You can generate such a key using the following Ruby code:
require 'securerandom' SecureRandom.hex(48) # => "f54ab...."
Later in this tutorial we assume that the Customer Master Key is
available from the CUSTOMER_MASTER_KEY
environment variable.
Warning
Using a local master key is insecure. It is recommended that you use a remote Key Management Service to create and store your master key. To do so, follow steps of the "Set up a Remote Master Key" in the MongoDB Client-Side Encryption documentation.
For more information about creating a master key, see the Create a Customer Master Key section of the MongoDB manual.
Configure Clients
Automatic CSFLE requires some additional configuration for the MongoDB client.
Assuming that your application has just one default
client, you need to
add the following to your mongoid.yml
:
development: clients: default: uri: mongodb+srv://user:pass@yourcluster.mongodb.net/blog_development?retryWrites=true&w=majority options: auto_encryption_options: # This key enables automatic encryption key_vault_namespace: 'encryption.__keyVault' # Database and collection to store data keys kms_providers: # Tells the driver where to obtain master keys local: # We use the local key in this tutorial key: "<%= ENV['CUSTOMER_MASTER_KEY'] %>" # Key that we generated earlier extra_options: crypt_shared_lib_path: '/path/to/mongo_crypt_v1.so' # Only if you use the library
Create a Data Encryption Key
A Data Encryption Key (DEK) is the key you use to encrypt the fields in your MongoDB documents. You store your Data Encryption Key in your Key Vault collection encrypted with your CMK.
To create a DEK in Mongoid you can use the
db:mongoid:encryption:create_data_key
Rake
task:
rake db:mongoid:encryption:create_data_key Created data key with id: 'KImyywsTQWi1+cFYIHdtlA==' for kms provider: 'local' in key vault: 'encryption.__keyVault'.
You can create multiple DEKs, if necessary.
rake db:mongoid:encryption:create_data_key Created data key with id: 'Vxr5m+5cQISjDOruzZgE0w==' for kms provider: 'local' in key vault: 'encryption.__keyVault'.
You can also provide an alternate name for the DEK. This allows you to reference the DEK by name when configuring encryption for your fields. It also allows you to dynamically assign a DEK to a field at runtime.
rake db:mongoid:encryption:create_data_key -- --key-alt-name=my_data_key Created data key with id: 'yjF8hKmKQsqGeFGXlB9Sow==' with key alt name: 'my_data_key' for kms provider: 'local' in key vault: 'encryption.__keyVault'.
Configure Encryption Schema
Now we can tell Mongoid what should be encrypted:
class Patient include Mongoid::Document include Mongoid::Timestamps # Tells Mongoid what DEK should be used to encrypt fields of the document # and its embedded documents. encrypt_with key_id: 'KImyywsTQWi1+cFYIHdtlA==' # This field is not encrypted. field :category, type: String # This field is encrypted using AEAD_AES_256_CBC_HMAC_SHA_512-Random # algorithm. field :passport_id, type: String, encrypt: { deterministic: false } # This field is encrypted using AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic # algorithm. field :blood_type, type: String, encrypt: { deterministic: true } # This field is encrypted using AEAD_AES_256_CBC_HMAC_SHA_512-Random # algorithm and using a different data key. field :ssn, type: Integer, encrypt: { deterministic: false, key_id: 'Vxr5m+5cQISjDOruzZgE0w==' } embeds_one :insurance end class Insurance include Mongoid::Document include Mongoid::Timestamps field :insurer, type: String # This field is encrypted using AEAD_AES_256_CBC_HMAC_SHA_512-Random # algorithm using the key which alternate name is stored in the # policy_number_key field. field :policy_number, type: Integer, encrypt: { deterministic: false, key_name_field: :policy_number_key } embedded_in :patient end
Note
If you are developing a Rails application, it is recommended to set
preload_models
to true
in mongoid.yml
. This will ensure that
Mongoid loads all models before the application starts, and the encryption
schema is configured before any data is read or written.
Known Limitations
MongoDB CSFLE has some limitations that are described on the CSFLE Limitations page in the Server manual. These limitations also apply to Mongoid.
Mongoid does not support encryption of
embeds_many
relations.If you use
:key_name_field
option, the field must be encrypted using non-deterministic algorithm. To encrypt your field deterministically, you must specify:key_id
option instead.
Working with Data
Automatic CSFLE usage is transparent in many situations.
Note
In code examples below we assume that there is a variable unencrypted_client
that is a MongoClient
connected to the same database but without encryption.
We use this client to demonstrate what is actually persisted in the database.
Documents can be created as usual, fields will be encrypted and decrypted according to the configuration:
Patient.create!( category: 'ER', passport_id: '123456', blood_type: 'AB+', ssn: 98765, insurance: Insurance.new(insurer: 'TK', policy_number: 123456, policy_number_key: 'my_data_key') ) # Fields are encrypted in the database unencrypted_client['patients'].find.first # => # {"_id"=>BSON::ObjectId('6446a1d046ebfd701f9f4292'), # "category"=>"ER", # "passport_id"=><BSON::Binary:0x404080 type=ciphertext data=0x012889b2cb0b1341...>, # "blood_type"=><BSON::Binary:0x404560 type=ciphertext data=0x022889b2cb0b1341...>, # "ssn"=><BSON::Binary:0x405040 type=ciphertext data=0x012889b2cb0b1341...>, # "insurance"=>{"_id"=>BSON::ObjectId('6446a1d046ebfd701f9f4293'), "insurer"=>"TK", "policy_number"=><BSON::Binary:0x405920 type=ciphertext data=0x012889b2cb0b1341...>}, "policy_number_key"=>"my_data_key"}
Fields encrypted using a deterministic algorithm can be queried. Only exact match queries are supported. For more details please consult the server documentation.
# We can find documents by deterministically encrypted fields. Patient.where(blood_type: "AB+").to_a # => [#<Patient _id: 6447e34d46ebfd3debdd9c39, category: "ER", passport_id: "123456", blood_type: "AB+", ssn: 98765>]
Encryption Key Management
Customer Master Keys
Your Customer Master Key is the key you use to encrypt your Data Encryption Keys. MongoDB automatically encrypts Data Encryption Keys using the specified CMK during Data Encryption Key creation.
The CMK is the most sensitive key in CSFLE. If your CMK is compromised, all of your encrypted data can be decrypted.
Important
Ensure you store your Customer Master Key (CMK) on a remote KMS.
To learn more about why you should use a remote KMS, see Reasons to Use a Remote KMS.
To view a list of all supported KMS providers, see the KMS Providers page.
- MongoDB CSFLE supports the following Key Management System (KMS) providers:
Any KMIP Compliant Key Management System
Local Key Provider (for testing only)
Data Encryption Keys
Data Encryption Keys can be created using the
db:mongoid:encryption:create_data_key
Rake
task. By default they are
stored on the same cluster as the database.
However, it might be a good idea to store the keys separately. This can be
done by specifying a key vault client in mongoid.yml
:
development: clients: key_vault: uri: mongodb+srv://user:pass@anothercluster.mongodb.net/blog_development?retryWrites=true&w=majority default: uri: mongodb+srv://user:pass@yourcluster.mongodb.net/blog_development?retryWrites=true&w=majority options: auto_encryption_options: key_vault_client: :key_vault # Client to connect to key vault # ...
Encryption Keys Rotation
You can rotate encryption keys using the rewrap_many_data_key
method
of the Ruby driver. This method automatically decrypts multiple data encryption
keys and re-encrypts them using a specified CMK. It then updates
the rotated keys in the key vault collection. This method allows you to rotate
encryption keys based on two optional arguments:
A filter used to specify which keys to rotate. If no data key matches the given filter, no keys will be rotated. Omit the filter to rotate all keys in your key vault collection.
An object that represents a new CMK. Omit this object to rotate the data keys using their current CMKs.
Here is an example of rotating keys using AWS KMS:
# Create a key vault client key_vault_client = Mongo::Client.new('mongodb+srv://user:pass@yourcluster.mongodb.net') # Or, if you declared the key value client in mongoid.yml, use it key_vault_client = Mongoid.client(:key_vault) # Create the encryption object encryption = Mongo::ClientEncryption.new( key_vault_client, key_vault_namespace: 'encryption.__keyVault', kms_providers: { aws: { "accessKeyId": "<IAM User Access Key ID>", "secretAccessKey": "<IAM User Secret Access Key>" } } ) encryption.rewrap_many_data_key( {}, # We want to rewrap all keys { provider: 'aws', master_key: { region: 'us-east-2', key: 'arn:aws:kms:us-east-2:...' } } )
Adding Automatic Encryption To Existing Project
MongoDB automatic CSFLE supports encryption in place. You can enable encryption for your existing database, and will still able to read unencrypted data. All data written to the database will be encrypted. However, as soon as the encryption is enabled, all query operations will use encrypted data:
# We assume that there are two documents in the database, one created without # encryption enabled, and one with encryption. # We can still read both. Patient.all.to_a # => # [#<Patient _id: 644937ac46ebfd02468e58c8, category: "ER", passport_id: "DE-1257", blood_type: "AB+", ssn: 123456>, # #<Patient _id: 644937c946ebfd029309b912, category: "ER", passport_id: "AT-1545", blood_type: "AB+", ssn: 987654>] # But when we query, we can see only the latter one. Patient.where(blood_type: 'AB+').to_a # => [#<Patient _id: 644937c946ebfd029309b912, category: "ER", passport_id: "AT-1545", blood_type: "AB+", ssn: 987654>]
If you want to encrypt the existing database, it can be achieved by reading and writing back all data, even without any changes. If you decide to do so, please keep the following in mind:
Validate the integrity of existing data for consistent fidelity. CSFLE is type sensitive - for example you cannot store integers in a field that is declared as
String
.For strings, make sure that empty values are always empty strings or just not set, but not
nil
(CSFLE doesn't support nativenull
).This operation requires application downtime.