Choose an encryption layer from a threat model, not from the vague requirement “encrypt the database.”
Threat Models: Disk Theft, Cloud Admin, Database Admin, Application Compromise, and Log Leakage
AtlasMart stores customer contact data, payment references, operational logs, and backups. Different attackers see different surfaces, so one encryption feature cannot protect every path.
Learning objectives
Build a threat model that distinguishes disk theft, cloud/provider access, database administration, application compromise, and log/backup leakage.
Map transport TLS, storage encryption, CSFLE, and Queryable Encryption to what each layer does and does not protect.
Trace where plaintext can still exist: trusted application memory, logs, crash dumps, analytics exports, search indexes, and key-management paths.
Distinguish ciphertext confidentiality from authorization, integrity, availability, and recoverability.
Choose the minimum encryption layer that protects a stated threat without inventing guarantees it cannot provide.
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-l1. The lab does not claim native
storage encryption; it uses Community specifically so the
edition boundary is visible. Host publication is loopback-only
on port 27181. 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. 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. Start with the attacker, not the checkbox
“Encrypt the database” is not a threat model. A stolen disk, a cloud administrator, a MongoDB database administrator, a compromised application process, and a verbose application logger observe different boundaries. Encryption only helps when the attacker is outside the boundary that owns the decryption key.
| Threat | What the attacker can see | Layer that can help | What remains exposed |
|---|---|---|---|
| stolen data disk / snapshot | WiredTiger files, journal, backups | Enterprise/Atlas at-rest encryption or encrypted storage | a live authorized database process can still decrypt |
| cloud/storage operator | provider disks/snapshots | Atlas/provider encryption; customer-managed keys can change key custody | application/server plaintext after decryption |
| database administrator | server queries, memory-visible values | CSFLE / Queryable Encryption with keys outside server trust | authorized application plaintext and side channels |
| compromised application | decryption keys and plaintext in process | encryption alone is insufficient | requires app isolation, IAM, secret controls, least privilege |
| logs/exports/backups | values copied outside primary collection | client-side encryption can keep protected fields ciphertext; redaction also required | unprotected fields and keys can still leak |
2. Encryption layers protect different boundaries
TLS protects data in transit between endpoints. At-rest encryption protects storage media after the database process has obtained its storage key. Client-Side Field Level Encryption (CSFLE) encrypts selected BSON values in the client before they reach MongoDB. Queryable Encryption (QE) is also client-side but adds server-maintained encrypted metadata so supported queries can operate without revealing the plaintext field to the server. None of these replaces authentication, authorization, backup, or key recovery.
| Layer | Plaintext at MongoDB server? | Queryable? | Typical key owner |
|---|---|---|---|
| TLS only | Yes, after transport termination | normal database queries | server/client TLS identities |
| Enterprise/Atlas at rest | Yes, while server is running | normal database queries | storage/KMS boundary |
| CSFLE random | No for encrypted field | no equality query on plaintext semantics | application/client KMS |
| CSFLE deterministic | No for encrypted field | equality by encrypting the query value; leaks equality patterns | application/client KMS |
| Queryable Encryption | No for encrypted field | configured equality/range; preview string patterns are separate | application/client KMS |
3. Make the edition boundary observable
The Community image should identify itself as Community. That evidence is important because native WiredTiger encryption at rest is an Enterprise capability outside Atlas. The lab does not “simulate Enterprise by renaming a flag”; it records the actual edition, then uses the later CSFLE/QE labs for Community-compatible client-side encryption.
docker rm -f atlasmart-ch23-l1 2>/dev/null || truedocker volume rm atlasmart-ch23-l1-db 2>/dev/null || truedocker run -d --name atlasmart-ch23-l1 \ -p 127.0.0.1:27181:27017 \ -v atlasmart-ch23-l1-db:/data/db \ mongodb/mongodb-community-server:8.3.8-ubuntu2204-slim \ --bind_ip_all --port 27017until mongosh "mongodb://127.0.0.1:27181/admin" --quiet --eval 'db.runCommand({ping:1}).ok' 2>/dev/null | grep -q 1; do sleep 1; donemongosh "mongodb://127.0.0.1:27181/admin" --quiet --eval ' printjson(db.adminCommand({buildInfo:1})); printjson(db.adminCommand({getParameter:1,featureCompatibilityVersion:1}));' mongosh "mongodb://127.0.0.1:27181/atlasmart" --quiet --eval ' db.customers.insertOne({customerId:"C-2301",email:"alice@example.test",supportNote:"refund requested"}); printjson(db.customers.findOne({customerId:"C-2301"})); const b=db.adminCommand({buildInfo:1}); printjson({version:b.version,modules:b.modules,allocator:b.allocator});'
The server can read plaintext because no client-side encryption was configured. An empty/non-enterprise module list on the pinned Community image supports the edition check; it does not prove how a separately licensed Enterprise deployment or Atlas cluster is configured.
4. The deliberately wrong design: put keys beside ciphertext
If the application repository contains the local master key, database URI, and an encrypted data export together, one repository leak can collapse the intended trust separation. Likewise, encrypting a field but logging the original request body creates a plaintext copy outside the encrypted collection. The repair is to separate key custody from data custody and to inventory every secondary data path.
from dataclasses import dataclass@dataclassclass Surface: name: str plaintext: bool key_present: boolsurfaces = [ Surface("mongodb data file", False, False), # only true after CSFLE/QE Surface("application memory", True, True), Surface("request log", True, False), Surface("key service", False, True), Surface("analytics export", True, False),]for s in surfaces: print(f"{s.name:20} plaintext={s.plaintext} key_present={s.key_present}")# The lab is an inventory aid, not an encryption implementation.
5. Production judgment
Choose encryption from an explicit attacker model. If disk theft is the threat, storage encryption and key custody matter. If the database administrator must not see a field, storage encryption is insufficient and client-side encryption is relevant. If the trusted application is compromised, a field key loaded into that process is no longer a boundary. Treat logs, caches, search/vector systems, backups, observability payloads, and data-science extracts as separate copies that need their own controls. The next lesson focuses specifically on the storage-key boundary and why Community, Enterprise, and Atlas must not be conflated.
Check your understanding
- Why does storage encryption not hide data from an authorized database administrator?
- What new trust boundary does CSFLE create?
- Does Queryable Encryption protect against a compromised application that can access the keys?
- Why is logging part of the encryption threat model?
- What does ciphertext confidentiality not guarantee?
Review the answers
1. Because the running database process obtains the storage key and presents plaintext to authorized queries.
2. The application/client and its KMS hold the key; MongoDB stores the encrypted BSON value without the plaintext key.
3. No. If the application can decrypt legitimately, compromise of that application can usually reach plaintext and keys.
4. A log can become an independent plaintext copy even if the primary database field is encrypted.
5. It does not itself guarantee authorization, integrity, availability, correct query semantics, or recoverability.
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