A leaky MongoDB is one of the worst possible operational mistakes — open instances on the public internet are ransomwared within hours. This post is the production security checklist: auth (SCRAM / x.509 / LDAP / Kerberos / AWS IAM), RBAC (built-in + custom roles), network isolation, TLS in transit, encryption at rest, Client-Side Field-Level Encryption (CSFLE), and the newer Queryable Encryption that keeps data encrypted on the server and still queryable.

Series map: 00 Foundations → … → 12 Change Streams → 13 Security (this post) → 14 Backup & Ops → … → 17 Drivers & Case Studies.


Learning Outcome

  • Enable authentication + create roles + users with least privilege.
  • Use TLS in transit + encryption at rest.
  • Apply network isolation (Atlas private endpoint / VPC peering / IP allowlist).
  • Use Client-Side Field-Level Encryption for sensitive fields.
  • Use Queryable Encryption when encrypted data must still be searchable.
  • Audit access via the audit log.
  • Apply the production-hardening checklist.

Chapter 1 — Auth — the basics

Seven MongoDB security layers from the app to the disk, how each is set up and what it gives you

Every production cluster runs with auth enabled. Self-hosted:

1
2
3
4
# /etc/mongod.conf
security:
authorization: enabled

Or mongod --auth. Restart, then create the first admin (the localhost exception allows this on a freshly-secured instance):

1
2
3
4
5
6
7
use admin
db.createUser({
user: "admin",
pwd: passwordPrompt(),
roles: [{ role: "root", db: "admin" }]
})

After that, every connection must authenticate:

1
2
mongodb://admin:pw@localhost:27017/?authSource=admin

Atlas: auth is mandatory; you can't disable it.


Chapter 2 — Authentication mechanisms

Mechanism Use
SCRAM (SCRAM-SHA-256) default; username + password
x.509 client certificate mutual TLS — preferred for service-to-DB inside private networks
LDAP (Enterprise / Atlas) central directory
Kerberos (Enterprise) Active Directory environments
AWS IAM (Atlas only) service authenticates using its IAM role — no password to leak
OIDC (Atlas, 7.0+) federate to Auth0, Okta, etc.

For Atlas in 2026, the modern stack is usually AWS IAM for AWS workloads + SCRAM for everything else, both gated behind a VPC endpoint.


Chapter 3 — RBAC — roles + users

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
db.createRole({
role: "app_orders_rw",
privileges: [
{ resource: { db: "shop", collection: "orders" },
actions: ["find", "insert", "update", "remove"] },
{ resource: { db: "shop", collection: "products" },
actions: ["find"] }
],
roles: []
})

db.createUser({
user: "app",
pwd: passwordPrompt(),
roles: [
{ role: "app_orders_rw", db: "shop" }
]
})

The user app can read+write orders and only read products. No access elsewhere.

Built-in roles (most-used)

Role Scope
read, readWrite per database
dbAdmin, dbOwner per database
readAnyDatabase, readWriteAnyDatabase cluster-wide
clusterAdmin, clusterMonitor, clusterManager cluster ops
backup, restore for backup tools
root superuser — keep one human emergency account; no app should use it

Apps should never run as root. Define a custom role per service, granting only the collections + actions it needs.

Principle of least privilege

For a typical service:

  • One read-write role on its own collections.
  • One read-only role on shared / reference collections.
  • No access elsewhere.

Even for an admin user, prefer dbOwner on the relevant DB rather than root.


Chapter 4 — TLS in transit

Every wire connection should be TLS-encrypted. Self-hosted:

1
2
3
4
5
6
net:
tls:
mode: requireTLS
certificateKeyFile: /etc/ssl/mongo.pem
CAFile: /etc/ssl/ca.pem

Client connection string:

1
2
mongodb+srv://...?tls=true

Atlas: TLS is mandatory + the cert is auto-managed. Connections without TLS are refused.

Disable plaintext / require TLS even for localhost — accidental cleartext on a shared host is a real risk.


Chapter 5 — Network isolation

Layer your defences:

  1. Atlas Private Endpoint (AWS PrivateLink / Azure Private Link / GCP Private Service Connect) — traffic stays on the cloud backbone; cluster has no public IP.
  2. VPC Peering — your VPC talks to the Atlas VPC over private addresses.
  3. IP allowlist — restrict which IPs can connect (a fallback, not a primary defence).
  4. Self-hosted — bind mongod to private interface only (bindIp: 127.0.0.1,10.0.0.10); never 0.0.0.0 on public-internet hosts.
  5. Firewall / Security Group — only your app subnets reach port 27017.

The infamous "unsecured Mongo on the internet" stories are always: default bind + no auth + no firewall. Avoid all three.


Chapter 6 — Encryption at rest

Self-hosted (Enterprise / Atlas):

  • WiredTiger native encryption — AES-256 with keys from KMIP, Vault, Azure Key Vault, GCP KMS, AWS KMS, or a local keyfile.
  • Atlas: enabled by default with customer-managed keys (CMK) optional on dedicated tiers.

Backups, snapshots, oplog files are all encrypted on disk.

Encryption at rest protects against disk theft. It does NOT protect against a logged-in attacker (they have the cluster's own decryption key, transparently). For that, you need field-level encryption (next).


Chapter 7 — Client-Side Field-Level Encryption (CSFLE)

Sensitive fields are encrypted by the driver before they leave the application. The server only ever sees ciphertext.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const { ClientEncryption } = require('mongodb');

// 1. set up the key vault (a special collection)
const keyVaultNamespace = "encryption.__keyVault";

// 2. config the client with CSFLE
const client = new MongoClient(uri, {
autoEncryption: {
keyVaultNamespace,
kmsProviders: { aws: { accessKeyId, secretAccessKey } },
schemaMap: {
"shop.users": {
bsonType: "object",
encryptMetadata: {
keyId: [/* dataKeyId */]
},
properties: {
ssn: { encrypt: { bsonType: "string", algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } },
dob: { encrypt: { bsonType: "date", algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Random" } },
email: { encrypt: { bsonType: "string", algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } }
}
}
}
}
});

Algorithm matters:

  • Deterministic — same plaintext → same ciphertext. Allows equality lookups (find({ email: "x" })).
  • Random — same plaintext → different ciphertext each time. Hides distribution + frequency but no equality lookups.

The driver intercepts queries + writes, encrypts/decrypts as needed. Server, admins, backups, replicas all see ciphertext only.

Key Management: data keys are encrypted by a master key held in your KMS (AWS KMS, Azure Key Vault, GCP KMS, KMIP). The key vault collection stores encrypted data keys; the master key never leaves the KMS.

Constraints with CSFLE:

  • Only equality queries on deterministically-encrypted fields.
  • No range / sort / aggregation on encrypted fields.
  • For "range queries on encrypted data" → use Queryable Encryption.

Chapter 8 — Queryable Encryption (GA in 7.0)

Queryable Encryption extends field-level encryption to support equality + range + prefix queries on encrypted data — without ever decrypting on the server.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
schemaMap: {
"shop.users": {
bsonType: "object",
properties: {
email: { encrypt: { bsonType: "string", queries: { queryType: "equality" } } },
salary: { encrypt: { bsonType: "long", queries: { queryType: "range", min: 0, max: 1_000_000 } } }
}
}
}

// then:
db.users.find({ email: "ada@x" }) // equality on encrypted field — works
db.users.find({ salary: { $gte: 50000, $lt: 100000 } }) // range on encrypted field — works

Use cases:

  • PII fields needing search (email lookup for password reset).
  • Salaries, balances, ages — range queries while keeping plaintext invisible to DBAs.
  • Compliance (HIPAA, GDPR) — data is encrypted to ops staff but still queryable to apps with the right key.

Caveats: slower than unencrypted queries; some index types unsupported; key management complexity. Plan carefully.

Atlas: Queryable Encryption is supported on dedicated tiers + recent Mongo versions.


Chapter 9 — Audit log

The audit log records authentication + authorization + operations.

Self-hosted (Enterprise):

1
2
3
4
5
6
auditLog:
destination: file
format: JSON
path: /var/log/mongodb/audit.log
filter: '{ atype: { $in: ["authenticate", "createCollection", "dropDatabase"] } }'

Atlas: audit logs available on dedicated tiers; shipped to S3, splunk, etc.

Standard ops to audit: privileged role grants, user creation, collection drops, failed logins, schema changes.


Chapter 10 — Secrets management

Never:

  • Commit connection strings with passwords to git.
  • Hardcode passwords in config files in the image.
  • Print connection strings in logs.

Use:

  • HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, GCP Secret Manager.
  • Kubernetes: Secret resources mounted as env vars or files.
  • For AWS workloads → IAM auth to Atlas → no password to manage.

Rotate secrets quarterly. Automate via your secrets manager's rotation hooks.


Chapter 11 — Production-hardening checklist

  • [ ] Auth enabled (authorization: enabled or Atlas).
  • [ ] No root for application users; least-privilege custom roles.
  • [ ] TLS in transit (Atlas: default; self-hosted: requireTLS).
  • [ ] No public IPs / no bindIp: 0.0.0.0 on internet-facing hosts.
  • [ ] Atlas: private endpoint OR VPC peering; IP allowlist as backup.
  • [ ] Encryption at rest enabled (default in Atlas).
  • [ ] CSFLE / Queryable Encryption for highly sensitive fields.
  • [ ] Secrets in a secrets manager, not in env / repo.
  • [ ] Audit log capturing privileged ops + failed logins.
  • [ ] Routine backups (Post 14) tested with restore drills.
  • [ ] MFA on Atlas accounts; SSO for org access.
  • [ ] Quarterly review of users, roles, IP allowlists, network rules.
  • [ ] Version current; security patches applied within agreed SLA.

Chapter 12 — Common pitfalls

Pitfall Cause Fix
Open Mongo on public IP default install + no auth + no firewall bind to private; auth; firewall
App user has root "to make it easier" refactor to least-privilege role
Password in connection string in git unaware secrets manager; pre-commit hook
Plaintext between app + DB TLS not enforced requireTLS
PII visible to DBAs only encryption at rest CSFLE / Queryable Encryption
Lost decryption key after team rotation KMS keys not in central management hold KMS keys in org-level KMS, not personal accounts
Auth allowed but no MFA on Atlas account hijack risk enforce org-wide MFA
No audit trail of privileged ops no audit log enabled enable + ship to SIEM
Backup files unencrypted backup pipeline outside Atlas encrypt backups; restrict access

Practice Exercises

Exercise 1 — Enable auth

In a local Mongo, enable auth. Create admin user. Create per-DB user shop_app with readWrite on shop. Confirm shop_app can't access admin DB.

Exercise 2 — Custom role

Define a role orders_rw_products_ro: read+write on orders, read-only on products. Test.

Exercise 3 — TLS local

Generate a self-signed cert with openssl. Configure local mongod to require TLS. Connect with tls=true&tlsAllowInvalidCertificates=true.

Exercise 4 — Atlas private endpoint

Set up a free Atlas cluster + AWS PrivateLink endpoint. Connect from an EC2 in the same VPC. Confirm public IPs can't connect.

Exercise 5 — CSFLE PoC

Wire up CSFLE with a local KMS file (for dev only). Insert a user with ssn (deterministic) + dob (random). Query by ssn — works. Query dob range — fails (need Queryable Encryption).

Exercise 6 — Audit log

Enable audit log on local mongod. Trigger a failed login. Inspect the log entry.


Cheat-sheet

What How
Enable auth security.authorization: enabled
Auth mechanism SCRAM (default); x.509 / LDAP / Kerberos / AWS IAM / OIDC
Least privilege custom role per service, narrow scope
TLS net.tls.mode: requireTLS + cert
Bind interface bindIp to private only
Atlas isolation private endpoint > peering > IP allowlist
Encryption at rest default in Atlas; WiredTiger encryption self-hosted
Field encryption (equality) CSFLE deterministic
Field encryption (random / better security, no query) CSFLE random
Field encryption + equality + range queryable Queryable Encryption (7.0+)
KMS AWS KMS / Azure KV / GCP KMS / KMIP; never local in prod
Secrets Vault / AWS Secrets / K8s Secret
Audit enable + ship to SIEM
MFA on Atlas org-wide enforcement
Don't root for apps; 0.0.0.0 bind; password in code

What's next

Post 14 covers backup + ops — mongodump / mongorestore, BSON snapshots, oplog tailing, point-in-time recovery (PITR), Atlas continuous backup, monitoring (profiler, currentOp, server-status), capacity planning, restore drills.


References

Note: this post is in progress. Planned additions: PCI / HIPAA pattern walkthrough; KMS key rotation procedure; OIDC + Okta wiring; egress lockdown patterns.