Skip to main content

Queryable Encryption

Queryable Encryption lets your application query encrypted data without exposing the underlying values to the database. Fields remain encrypted everywhere outside your application, including when they are stored and queried on the cluster. Your application encrypts values before sending them to MongoDB, and decrypts them when they are returned. This allows you to search encrypted fields while keeping sensitive data protected.

How it compares with other encryption mechanisms​

Each mechanism protects a different stage of the data's life.

MechanismProtects dataServer sees plaintext
SSL/TLSIn transitYes
Disk encryption (TDE)At restYes, the server decrypts it to work with it
Queryable EncryptionIn transit, at rest, and in useNo

The three are complementary, not alternatives. Only the last column changes what someone with read access to the database can see.

How it works​

Your application code does not change shape. You write {ssn: "123-45-6789"} and you read it back the same way, while the driver and a library called crypt_shared encrypt on the way out and decrypt on the way in.

Encrypted insert and find flow between the application and the cluster

The middle row is what makes this more than ordinary client-side encryption. The driver encrypts the search filter too, so the server matches ciphertext against ciphertext and returns the right documents without learning what you searched for.

Which queries still work​

Matching only works for the query type you declare when you create the collection, and that choice is permanent for the life of the collection.

Query typeUse forStatus
equality$eq, $in, $neProduction-ready from MongoDB 7.0
range$lt, $lte, $gt, $gteProduction-ready from MongoDB 8.0
prefix, suffix, substringPrefix, suffix, and substring searchProduction-ready from MongoDB 9.0
noneEncrypt a field without querying itAlways available
What this means on FPT Cloud

Version 8.0.20 gives you equality and range, while 7.0.30 gives you equality only. Version 6.0.27 cannot be used at all, because MongoDB 6.0 shipped Queryable Encryption as a public preview that is no longer supported.

Limitations to plan around​

Encrypting a field narrows what you can do with it. Read this list against your application's real queries before you settle on a schema.

The first limitation shapes everything else. You can enable Queryable Encryption only on a new, empty collection, and you cannot change which fields are encrypted afterwards. Getting it wrong means creating a new collection and migrating the data.

The rest apply to the encrypted fields themselves.

  • A collection cannot use Queryable Encryption and CSFLE at the same time.
  • collation is not supported.
  • updateMany and deleteMany are not supported, and bulkWrite is limited to a single namespace.
  • Updates to an encrypted field accept $set and $unset only, not $inc or $push.
  • findOneAndUpdate and findOneAndReplace must return the document as it was before the change (returnDocument: before) and cannot use a projection.
  • Querying an encrypted field with null, with a regular expression, or against another encrypted field fails.
  • In aggregation, $out and $merge are unavailable, and $group works on unencrypted fields only.
  • distinct is unavailable.
  • Drop the collection with a client that has Queryable Encryption enabled, otherwise the metadata collections are left behind.

The full list is in MongoDB's Supported Operations for Queryable Encryption.

Setup steps​

The sequence is the same in every language, and the two pages that follow work through it line by line.

Five setup steps, from creating the master key to reading and writing through the encrypting client

Create the master key​

For a trial this is a local file holding 96 random bytes, read once when the application starts. Production replaces it with a KMS (AWS, Azure, GCP, or KMIP).

Declare the fields​

Each encrypted field needs a path, a bsonType, and a queryType. The queryType decides which operators still work on that field, and it cannot be changed once the collection exists.

Create the collection​

One call does three things. The driver generates a Data Encryption Key per field, the server creates the collection, and the server adds the metadata collections enxcol_.<COLLECTION>.esc and enxcol_.<COLLECTION>.ecoc next to it. The collection must not already exist.

Build the encrypting client​

This is a second client, separate from the plain one that created the collection. It takes the field map returned by step 3, which by now carries the key IDs the driver generated. Steps 3 and 4 have to happen in that order, because the field map only gains those key IDs once the collection exists.

Read and write​

From here the collection behaves like any other. The driver encrypts the declared fields before they leave the process and decrypts them when the documents come back.

danger

Data encrypted with a lost master key is unrecoverable. Back the key file up somewhere separate from the database, and move to a KMS before you put real data behind it.

Checking that it works​

Once the script runs, the same document looks completely different depending on which client reads it.

  • Through the encrypted client, ssn reads back as plaintext.
  • Through an ordinary client, or mongosh without the key, ssn is BinData subtype 6, and the document carries an extra __safeContent__ field.
  • Searching for the plaintext value through an ordinary client returns nothing at all.

Common errors​

The table maps each message to what causes it.

ErrorCause and fix
mongo_crypt_v1.so not foundThe path is wrong, or crypt_shared is not installed on the application host
A crypt_shared version errorcrypt_shared must be the same version as the server or newer
Multi-document updates are not allowed with Queryable EncryptionThe code calls updateMany. Use updateOne
Invalid match expression operator on encrypted fieldThe operator is not supported for that field's query type, such as a regex or $gt on an equality field
findAndModify with encryption only supports new: falseThe call uses returnDocument: after. Change it to before
The collection already existsQueryable Encryption needs an empty new collection. Drop it with an encryption-enabled client first
Authentication fails on connectThe password contains characters that were not URL-encoded

Recommendations for production​

The examples use a local key file to keep the first run simple. Change these before real data goes in.

  • Use a KMS (AWS, Azure, GCP, or KMIP) instead of a local key. A local key sits in a file, so whoever obtains that file can decrypt everything it protects.
  • Keep passwords out of source code. Read them from environment variables or a secret manager.
  • Encrypt only the fields that genuinely need it. Each encrypted field costs query flexibility and storage, through the enxcol_.* metadata collections and __safeContent__.
  • Decide which fields need queries before you create the collection, since that choice cannot be revised later.
  • Test the application's real queries against the supported operator list before you deploy, not after.

References​

The pages below are MongoDB's own documentation, and they go further than this guide does.

Next steps​