Make CSFLE observable from the local Customer Master Key through the key vault, ciphertext BinData, deterministic/random algorithms, and driver decryption.
Client-Side Field Level Encryption: Key Vaults, KMS, Automatic Encryption, and Driver Responsibilities
AtlasMart wants the database server to store customer email and support notes as ciphertext while a trusted application can decrypt them. The Community-compatible learning path therefore uses explicit CSFLE with a disposable local master key.
Learning objectives
Explain the CSFLE key hierarchy: Customer Master Key, Data Encryption Key, key vault, and encrypted BSON BinData.
Use Community-compatible explicit CSFLE with a generated local master key and PyMongo encryption support.
Compare deterministic and random CSFLE algorithms, including equality-pattern leakage and query consequences.
Inspect key-vault metadata and prove that an ordinary MongoDB client sees ciphertext while a configured client can decrypt.
Distinguish explicit encryption from automatic encryption and state the Enterprise/Atlas query-analysis boundary.
This lesson pins
MongoDB Community Server 8.3.8 with
mongodb/mongodb-community-server:8.3.8-ubuntu2204-slim, mongosh 2.10.0, and
PyMongo 4.17.0 where driver behavior is used.
Topology: one disposable Community standalone named
atlasmart-ch23-l3. Explicit CSFLE is supported on
Community. The local master key is generated at runtime for
learning only; production should use an appropriately protected
remote KMS or another secure key-injection design. Host
publication is loopback-only on port 27183.
Authentication/TLS state is stated next to the experiment.
Default read/write concern and primary read preference are used
unless noted.
FCV is observed and never changed. The local
KMS examples generate disposable 96-byte master keys at runtime
and never embed a real cloud/KMIP credential. Atlas, Enterprise
Advanced, AWS KMS, Azure Key Vault, GCP KMS, KMIP, and HSMs are
optional production paths. PyMongo CSFLE requires the encryption
dependency; current PyMongo upgrade guidance requires
pymongocrypt 1.10+ for supported CSFLE use. Product
runtime labs were not executed in the generation environment;
ciphertext values, key identifiers, timings, storage
amplification, and recovery results must be measured locally
rather than copied as invented output.
1. The key vault stores wrapped DEKs, not the plaintext master key
The Customer Master Key (CMK) lives in the
configured Key Management System (KMS) boundary. The driver uses
it to wrap a Data Encryption Key (DEK). MongoDB
stores the wrapped DEK as a key-vault document. The application
retrieves that wrapped material and asks the configured KMS
provider to unwrap it. A key alternate name is convenient, but
the key-vault namespace also needs a unique partial index on
keyAltNames so two DEKs cannot claim the same name.
| Object | Stored where in this local lab? | Production expectation |
|---|---|---|
| CMK | ephemeral local file outside MongoDB | remote KMS/HSM or hardened key injection |
| DEK document | encryption.__keyVault |
back up key vault with database; protect access |
| encrypted field | atlasmart.customers_csfle |
ciphertext remains in normal database/backups |
| plaintext | trusted Python process only | keep out of logs/crash dumps/telemetry |
2. Build the explicit-CSFLE lab without committing a key
docker rm -f atlasmart-ch23-l3 2>/dev/null || truedocker volume rm atlasmart-ch23-l3-db 2>/dev/null || truedocker run -d --name atlasmart-ch23-l3 \ -p 127.0.0.1:27183:27017 \ -v atlasmart-ch23-l3-db:/data/db \ mongodb/mongodb-community-server:8.3.8-ubuntu2204-slim \ --bind_ip_all --port 27017until mongosh "mongodb://127.0.0.1:27183/admin" --quiet --eval 'db.runCommand({ping:1}).ok' 2>/dev/null | grep -q 1; do sleep 1; donemongosh "mongodb://127.0.0.1:27183/admin" --quiet --eval ' printjson(db.adminCommand({buildInfo:1})); printjson(db.adminCommand({getParameter:1,featureCompatibilityVersion:1}));' python -m venv /tmp/atlasmart-ch23-venv. /tmp/atlasmart-ch23-venv/bin/activatepython -m pip install --disable-pip-version-check "pymongo[encryption]==4.17.0"python - <<'PY'import os, pathlibpath=pathlib.Path('/tmp/atlasmart-ch23-master-key.bin')path.write_bytes(os.urandom(96))os.chmod(path,0o600)print('generated_bytes=',path.stat().st_size)PY
from pathlib import Pathfrom bson.codec_options import CodecOptionsfrom pymongo import MongoClientfrom pymongo.encryption import ClientEncryptionURI = "mongodb://127.0.0.1:27183"KEY_VAULT = "encryption.__keyVault"client = MongoClient(URI)key_vault = client.encryption.__keyVaultkey_vault.create_index( "keyAltNames", unique=True, partialFilterExpression={"keyAltNames": {"$exists": True}},)local_master_key = Path('/tmp/atlasmart-ch23-master-key.bin').read_bytes()kms = {"local": {"key": local_master_key}}ce = ClientEncryption(kms, KEY_VAULT, client, CodecOptions())key_id = ce.create_data_key("local", key_alt_names=["atlasmart-customer-pii"])email = "alice@example.test"note = "customer requested a refund"email_ct = ce.encrypt(email, "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", key_id=key_id)note_ct = ce.encrypt(note, "AEAD_AES_256_CBC_HMAC_SHA_512-Random", key_id=key_id)coll = client.atlasmart.customers_csflecoll.delete_many({})coll.insert_one({"customerId":"C-2303", "email":email_ct, "supportNote":note_ct})raw = coll.find_one({"customerId":"C-2303"})print('raw_email_type=', type(raw['email']).__name__)print('raw_note_type=', type(raw['supportNote']).__name__)print('decrypted_email=', ce.decrypt(raw['email']))print('decrypted_note=', ce.decrypt(raw['supportNote']))query_ct = ce.encrypt(email, "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", key_id=key_id)print('deterministic_match=', coll.count_documents({"email": query_ct}))print('key_alt_names=', key_vault.find_one({"_id": key_id})["keyAltNames"])
3. Deterministic and random CSFLE make different leakage/query tradeoffs
Random encryption should produce different ciphertext for the same plaintext across encryptions and therefore does not support equality lookup by encrypting the query value. Deterministic encryption maps the same value under the same key/algorithm to the same ciphertext, which enables equality matching but leaks equality/frequency patterns. That leakage is one reason Queryable Encryption uses a different cryptographic design for searchable encrypted fields.
# Continue in the same script after creating ce/key_id.d1 = ce.encrypt("same", "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", key_id=key_id)d2 = ce.encrypt("same", "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", key_id=key_id)r1 = ce.encrypt("same", "AEAD_AES_256_CBC_HMAC_SHA_512-Random", key_id=key_id)r2 = ce.encrypt("same", "AEAD_AES_256_CBC_HMAC_SHA_512-Random", key_id=key_id)print('deterministic_equal=', d1 == d2)print('random_equal=', r1 == r2)
4. Automatic CSFLE is a different operational mode
With automatic encryption, a configured driver
analyzes outgoing commands and encrypts/decrypts fields
according to a schema. Current compatibility documentation keeps
automatic CSFLE on Atlas/Enterprise while explicit CSFLE is
available on Community. The Automatic Encryption Shared Library
is the modern query-analysis component; older deployments can
encounter mongocryptd. Do not make “the driver will
encrypt it” an implicit assumption—record whether the
application is explicit or automatic and fail closed if the
encryption configuration cannot initialize.
Delete or replace the local master-key file only after copying the ciphertext/key-vault state into the disposable lab. A subsequent decrypt should fail. Restore the original 96-byte key and retry. This demonstrates the recoverability dependency without touching any real key or production data.
5. Production judgment
CSFLE moves selected plaintext out of the database trust boundary, but the trusted application becomes more sensitive: it holds or can obtain keys and plaintext. Protect application memory, logs, KMS credentials, deployment pipelines, and key-vault permissions. Deterministic CSFLE may be acceptable for equality-search needs when its leakage model is understood; random encryption is stronger against frequency observation but not directly queryable. The next lesson uses Queryable Encryption when the application needs server-assisted encrypted equality/range queries without deterministic ciphertext semantics.
Check your understanding
- What does the key vault store?
- Why is the keyAltNames index unique and partial?
- What leakage does deterministic CSFLE introduce?
- Can Community perform explicit CSFLE?
- Why is a local master-key file a lab-only choice?
Review the answers
1. Wrapped data-encryption key documents and metadata such as alternate names; it does not store the local/cloud Customer Master Key itself.
2. It prevents ambiguous alternate names while indexing only key documents that actually have alternate names.
3. Equal plaintext values encrypted with the same key/algorithm produce equal ciphertext, revealing equality/frequency patterns.
4. Yes. Current compatibility supports explicit CSFLE on Community; automatic encryption/query analysis has a different Enterprise/Atlas boundary.
5. It weakens key separation and can be lost or copied with the application; production should use a protected remote KMS/HSM or hardened injection design.
Authoritative references
Version-, edition-, driver-, topology-, and preview-sensitive encryption behavior was checked against current MongoDB documentation at generation time. Re-check these sources before applying the procedure to a later release.
- MongoDB Encryption
- Configure Encryption at Rest
- Atlas Encryption at Rest
- Client-Side Field Level Encryption
- CSFLE Compatibility
- CSFLE Encryption Schemas
- Queryable Encryption
- Queryable Encryption Compatibility
- Queryable Encryption Features
- Queryable Encryption Supported Operations
- Encrypted Fields and Enabled Queries
- Create an Encryption Schema
- Explicit Queryable Encryption
- Queryable Encryption KMS Providers
- Rotate and Rewrap Encryption Keys
- Key Vault createKey
- Key Vault getKeyVault
- MongoClient Queryable Encryption Options
- PyMongo CSFLE Upgrade Requirements
- MongoDB 8.3 Release Notes
- mongosh Release Notes
- PyMongo Release Notes