Securing MongoDB with SCRAM authentication, building RBAC-based authorization with built-in roles and custom roles, and protecting data through encryption at rest, TLS for connections, and client-side field level encryption for sensitive fields.

So far you've run MongoDB without authentication — reasonable for a learning lab, fatal for production. An unsecured database is a time bomb: anyone who can reach port 27017 can read, change, or delete all data. Episode 17 closes these security holes thoroughly.
The roadmap: first we turn on authentication by creating an admin user and --auth mode, second we build authorization with RBAC — built-in roles and custom roles, third we protect data with encryption at rest, fourth TLS for connection encryption, and fifth client-side field level encryption for the most sensitive fields. Let's get started.
Before enabling authentication, you must create an admin user — otherwise no one will be able to log in. The process: start the server without auth, create a user in the admin database, then enable auth:
mongosh "mongodb://localhost:27017"use admin
db.createUser({
user: "admin",
pwd: passwordPrompt(),
roles: [{ role: "root", db: "admin" }]
})passwordPrompt() asks for the password interactively — never put a password in the command line. The root role gives full privileges over the cluster.
Once the admin user exists, turn on authentication by starting mongod with the --auth flag:
mongod --auth --dbpath /data/db --port 27017For a native systemd installation, add --auth in the mongod.conf configuration file:
security:
authorization: enabledAfter this, all connections must log in. Connections from mongosh now require credentials:
mongosh "mongodb://admin:rahasia@localhost:27017/admin"MongoDB supports several authentication mechanisms:
Authentication only proves identity; authorization determines what that identity is allowed to do. MongoDB uses RBAC: users are given roles, and roles contain sets of privileges on specific databases/collections.
MongoDB provides built-in roles for common needs:
| Role | Privileges |
|---|---|
read | Read collections in the database |
readWrite | Read and write |
dbAdmin | Manage the database (indexes, collections, validators) |
userAdmin | Manage users and roles in the database |
clusterAdmin | Manage the cluster: sharding, replica sets, backups |
root | All privileges on all databases |
db.getSiblingDB("app").createUser({
user: "appUser",
pwd: passwordPrompt(),
roles: [{ role: "readWrite", db: "app" }]
})The best practice is clear: grant the least privilege that is sufficient. An application only needs readWrite on its database; never run an application as root.
Built-in roles are often too broad or too narrow. For granular privileges, define custom roles:
db.getSiblingDB("app").createRole({
role: "orderViewer",
privileges: [
{
resource: { db: "app", collection: "orders" },
actions: ["find", "count"]
}
],
roles: []
})The orderViewer role above can only read (find, count) the orders collection — can't write, can't touch other collections. This is a minimal-privilege example worth applying in teams with many developers.
Encryption at rest protects physical data on disk — if a disk is stolen or a snapshot leaks, the data remains unreadable without the key. Two common approaches:
security:
enableEncryption: true
encryptionKeyFile: /etc/mongodb-keys/keyfileEncryption at rest protects data on disk, but connections between the application and MongoDB flow as plain text — they can be intercepted on the network. The solution is TLS/SSL to secure two paths:
mongosh "mongodb://localhost:27017/?tls=true&tlsCAFile=/etc/ssl/ca.pem"In production deployments, enable TLS everywhere — drivers, mongod, mongos, and between nodes. Without TLS, all the other defenses (auth, disk encryption) still leave data vulnerable in transit.
The highest level of encryption: CSFLE encrypts sensitive fields on the application side before the data is sent to the server. The server only sees ciphertext — even the database admin can't read the original values. This protects data from insider threats and satisfies strict compliance requirements (national ID numbers, card numbers, medical data).
How it works: the application defines a schema that marks encrypted fields, encryption keys are stored in an external KMS (AWS KMS, Azure Key Vault, etc.), and the driver encrypts those fields transparently. The MongoDB server stores and indexes the ciphertext without ever seeing the plaintext.
Info
MongoDB's layered view of security: authentication ensures legitimate users, RBAC limits their privileges, TLS secures connections, encryption at rest secures the disk, and CSFLE secures the most sensitive fields even from the database admin. The more sensitive the data, the more layers should be active. Start with auth + RBAC + TLS as the absolute production minimum, then move up to encryption as needed.
Warning
With --auth enabled, don't forget to document and secure the admin credentials. Losing the admin password = locked out of your own data; leaking the root password = the cluster fully in an attacker's hands. Store credentials in a secret manager (not in repo files!), rotate passwords regularly, and give every application its own user — not a shared root user.
In episode 17 you secured MongoDB in layers: enabling authentication with an admin user and --auth mode plus the SCRAM-SHA-256, x.509, and LDAP mechanisms; building RBAC-based authorization with the read, readWrite, dbAdmin, up to root built-in roles as well as custom roles for granular privileges; protecting data with encryption at rest via WiredTiger or LUKS; securing connections with TLS for client-to-server and intra-cluster; and using client-side field level encryption for the most sensitive fields.
Key takeaways:
--auth.root.In the next episode, episode 18, we prepare a rescue plan: Backup, Restore & Change Streams. You'll use mongodump and mongorestore for logical backups, filesystem snapshots for physical backups, get to know point-in-time restore in Atlas, and leverage change streams to listen to data changes in real time. See you in episode 18!