This tutorial was written by MongoDB Champion Humberto Diaz.
One of Rails' doctrinal pillars puts programmer happiness on a pedestal. Convention over configuration, human-readable code, and smart defaults make the Rails Way feel almost intuitive. With the release of Mongoid 9.0, that same philosophy now extends to data encryption. Tasks that once required multi-step configurations can now be completed through a single model declaration. MongoDB’s Client-Side Field-Level Encryption (CSFLE) can now be seamlessly integrated into your applications, enhancing your data security with the ease and simplicity developers expect from Rails.
Why client-side encryption matters
Traditional encryption occurs on the server or at rest in the database. This means that your data, while protected on disk, is still visible as plain text when traveling over the network. This would be similar to writing a secret note to a friend and simply folding the note in half before sending it in the mail. With little effort, anyone could potentially read your message as it makes its way to your friend.
Client-Side Field Level Encryption changes this completely as your Rails app encrypts data before it ever leaves the application. Not even your MongoDB cluster will see the unencrypted values! Only your app, which holds the encryption key, can decrypt it. Referring back to the previous analogy, instead of your note traveling unprotected, it’s now in a locked box, and your friend is the only one with the matching key to open it.
What this means:
- Accidental leaks of sensitive information are less likely.
- Unauthorized users with DB access see nothing but ciphertext.
Ultimately, your database becomes zero-knowledge, storing data without knowing what it is storing.
The problem before Mongoid 9.0
The MongoDB Ruby Driver has supported CSFLE since v2.12, but Ruby on Rails developers don’t work with the driver directly—they use Mongoid.
You had to:
- Write custom logic for encryption/decryption.
- Manually manage keys through the driver API.
- Keep a log of which fields needed encryption.
This is not the Rails Way.
The Mongoid 9.0 solution
Mongoid 9.0 introduces automatic client-side field-level encryption, using a simple declarative way to encrypt data that feels right at home in Rails.
Inspired by Active Record Encryption, it lets you define encrypted
fields directly in your models:
class BankAccount
include Mongoid::Document
include Mongoid::Timestamps
encrypt_with key_id: 'KImyywsTQWi1+cFYIHdtlA=='
field :account_number, type: String, encrypt: { deterministic: false }
field :bank_name, type: String, encrypt: { deterministic: false }
field :name, type: String
field :account_type, type: String
end
That’s it! There are no service objects, no callbacks, no extra encryption logic. When you save an instance of your model, Mongoid encrypts the appropriate fields. When you load data into your models, Mongoid decrypts the relevant fields.
It’s invisible, automatic, and secure.
Setting it up in Rails
You can get up and running in just a few minutes.
1. Install the required gems
gem 'mongoid', '>= 9.0'
gem 'libmongocrypt-helper'
Then run:
bundle install
Note: The
libmongocrypt-helperis great for experimentation but should only be used in development. For production environments, install the official MongoDB shared library (crypt_shared) and ensure you’re using MongoDB Ruby Driver v2.19 or later. Detailed installation instructions can be found in the Mongoid Shared Library.
2. Generate a local master key and add encryption settings to config/mongoid.yml
We will use a local key provider for testing (see “Create a Customer Master Key”), and store in a LOCAL_MASTER_KEY environment variable, which we’ll use to configure our Ruby on Rails application:
export LOCAL_MASTER_KEY=$(ruby -e "require 'securerandom'; puts SecureRandom.hex(48)")
Make sure you save this key somewhere safe, as it is only generated once. Next, we’ll modify the config/mongoid.yml file to include our key:
development:
clients:
default:
uri: mongodb+srv://user:pass@yourcluster.mongodb.net/test
options:
auto_encryption_options:
key_vault_namespace: 'encryption.__keyVault'
kms_providers:
local:
key: "<%= ENV['LOCAL_MASTER_KEY'] %>"
Note: The example above uses a local KMS provider. For production environments, configure a key management system (KMS) such as AWS KMS or Azure Key Vault. For more information, see KMS Providers.
3. Generate a data encryption key (DEK)
$ rake db:mongoid:encryption:create_data_key
Created data key with id: 'KImyywsTQWi1+cFYIHdtlA==' for kms provider: 'local' in key vault: 'encryption.__keyVault'.
Copy the key ID and plug it into your model’s encrypt_with call.
encrypt_with key_id: 'KImyywsTQWi1+cFYIHdtlA=='
With that, your application will now encrypt the selected fields automatically!
Seeing it in action
MongoDB has a sample Mongoid CSFLE application that can be cloned and tested to quickly showcase the flexibility and power of this feature.
Once the repository has been cloned, all you need is a connection string to your Atlas cluster in an environment variable to get up and running:
bundle install
export ATLAS_URI=<your MongoDB Atlas connection string>
export LOCAL_MASTER_KEY=$(ruby -e "require 'securerandom'; puts SecureRandom.hex(48)")
export USER_KEY_ID=$(rails db:mongoid:encryption:create_data_key)
rails db:seed
rails server
This will spawn a Rails server on http://127.0.0.1:3000 that you can begin interacting with.
Note: Two users are available by default: john@doe.com and jane@doe.com (both with a password of
111111).
Once logged in, new accounts and transactions can be recorded. When you initially seed the application, two accounts and a number of transactions are populated.
Since this data is encrypted client-side, we can interact with it directly from our application, or through model instances from the Rails console as we normally would:
BankAccount.create!(
name: "Chequing Account",
account_number: "4982-2848747",
bank_name: "TD Canada Trust",
account_type: "Chequing"
)
BankAccount.last.account_number
# => "4982-2848747"
But if you inspect the same document in mongosh…
db.bank_accounts.find().sort({ _id: -1 }).limit(1)
[
{
_id: ObjectId('66732094a7ea7521fffe9c57'),
name: 'Chequing Account',
account_number: Binary.createFromBase64('AiIMmmNzmkjahqJVcfByoSsC8svek5AUPu+OB7IAc92cFfia3ezc+m/d05fji0EGhp/DNF7ng5oO1C5Qe0b8vTDD2U4zjXRsX1e6b+Le0xkIngfTxlKqEiamrUZkenTmlQ8=', 6),
account_type: 'Chequing',
bank_name: Binary.createFromBase64('AiIMmmNzmkjahqJVcfByoSsCgGgZ2bENMFyVTeUz1F93+arEbaJIQjBVnnISHMhOo3M1PvRfl6jNiisfK3c6SvepxzfyyGkBNYnhVWKkLAtOkKio9V4ZHOfPMIYyrxgnd1k=', 6),
user_id: ObjectId('66731cf9a7ea751d4555faf6'),
updated_at: ISODate('2024-06-19T18:16:52.859Z'),
created_at: ISODate('2024-06-19T18:16:52.859Z'),
encryption_key_name: '7c815580ad3844bcb627c74d24eaf700e1a711d9c23e9beb62ab8d28e8cb7954'
}
]
…you’ll see unreadable binary data—proof that encryption happens before MongoDB ever receives the record.
Deterministic vs. randomized encryption
Mongoid lets you choose how each field is encrypted:
So you can safely do:
BankAccount.where(account_number: "123456789")
If that field uses deterministic encryption.
For more variations on how our model can be configured, see Mongoid’s automatic client-side field level encryption tutorial.
Seamless Rails integration
Ultimately, Mongoid’s CSFLE support feels native to the Rails ecosystem:
- It automatically works with Rails validations, associations, and timestamps.
- It helps keep your codebase clean. Encryption is simply a declaration, not a responsibility. Developers only have to describe what should happen, not write out how it should happen.
- It supports Rails-style configuration (mongoid.yml, environment variables). No special setup is required, no additional configuration syntax—it all just works exactly how Rails developers expect.
Developers are free to focus on their app’s logic. Meanwhile, Mongoid takes care of security behind the scenes.
What you get out of the box
- Automatic encryption and decryption on read or write actions
- Multi-KMS support (AWS, Azure, GCP, KMIP, or local)
- Transparent key vault management
- No schema migrations—just annotate fields in your model
- Compliance-ready architecture without third-party gems
Final thoughts
Client-side encryption was once something only security engineers dealt with, but with Mongoid 9.0, it’s now firmly in the Rails developer toolbox. You can add enhanced security to your app with a few lines of YAML and a single model declaration. There is no custom cryptography and no painful boilerplate.
Your data is always private, your code stays clean, and your app stays fast. That’s truly the Rails Way.



Top comments (0)