Security, Users and Roles

Lock down a MongoDB deployment: enable authentication, create users with built-in and custom roles following least privilege, restrict network access, encrypt traffic with TLS and data at rest, audit activity, and protect applications from NoSQL injection.

Lesson 24 of 43Advanced17 min read

Every year, thousands of MongoDB databases are found exposed on the internet with no password, their data copied or held to ransom. Almost all of those incidents come from a handful of avoidable mistakes: authentication switched off, the server listening on a public address, and applications connecting as an all-powerful admin user.

A secure deployment combines several layers. Authentication makes every client prove who it is. Authorisation with roles limits what each user can do, following the principle of least privilege: the web application can read and write its own database, the reporting tool can only read, and only administrators can manage users. Network controls make the database reachable only from your application servers. Encryption protects data travelling over the network and stored on disk. Auditing records who did what.

MongoDB Atlas applies many of these protections by default, but you still choose users, roles and network access. In this lesson you'll create users with built-in and custom roles, restrict network access, understand encryption options, and protect your application code from NoSQL injection.

Authentication and users

On a self-managed server, turn on access control in the configuration file:

Terminal
# mongod.conf
security:
  authorization: enabled
net:
  bindIp: 127.0.0.1,10.0.1.12

Then create users. Users are defined in a database, often admin, and receive roles:

MongoDB Shell
use admin
db.createUser({ user: 'siteAdmin', pwd: passwordPrompt(), roles: ['userAdminAnyDatabase'] })

use shop
db.createUser({ user: 'shopApp', pwd: passwordPrompt(), roles: [{ role: 'readWrite', db: 'shop' }] })
  • passwordPrompt() asks for the password instead of typing it into your shell history.
  • Applications authenticate with the user in their connection string: mongodb://shopApp:...@db1:27017/shop?authSource=shop.

Built-in and custom roles

RoleCan
readRead all collections in one database
readWriteRead and write data in one database
dbAdminManage indexes and statistics, not data
userAdminManage users and roles in one database
clusterMonitorRead monitoring information
backup, restoreRun backup and restore tools
rootEverything, for emergencies only

When built-in roles are too broad, create a custom role with exactly the privileges needed, such as reading orders and updating only orders.status-related collections:

MongoDB Shell
db.createRole({
  role: 'orderClerk',
  privileges: [{ resource: { db: 'shop', collection: 'orders' }, actions: ['find', 'update'] }],
  roles: []
})

Network, encryption and auditing

  • Bind MongoDB only to private network interfaces, and use firewalls, security groups or Atlas IP access lists so only application servers can connect.
  • Use TLS so connections are encrypted; Atlas requires it.
  • Encryption at rest protects data files and backups; Atlas encrypts storage by default.
  • Client-side field level encryption and Queryable Encryption encrypt sensitive fields, such as national ID numbers, in the application, so even database administrators can't read them.
  • Auditing (MongoDB Enterprise and Atlas) records authentication, user changes and data access.
  • Keep connection strings and passwords in a secret manager or environment variables, and rotate them if they leak.

MongoDB Operations Syntax

Security commands

db.createUser({ user: 'app', pwd: passwordPrompt(), roles: [{ role: 'readWrite', db: 'shop' }] })
db.grantRolesToUser('app', [{ role: 'read', db: 'reports' }])
db.revokeRolesFromUser('app', [{ role: 'read', db: 'reports' }])
db.changeUserPassword('app', passwordPrompt())
db.dropUser('oldScript')
db.getUsers()
db.createRole({ role: 'statusUpdater', privileges: [{ resource: { db: 'shop', collection: 'orders' }, actions: ['find', 'update'] }], roles: [] })
db.runCommand({ connectionStatus: 1 }).authInfo

MongoDB Operations Summary

  • Enable authentication and access control, and never expose MongoDB to the public internet.
  • db.createUser creates users with roles; passwordPrompt keeps passwords out of shell history.
  • Follow least privilege: read, readWrite and narrow custom roles instead of admin roles for apps.
  • Restrict network access, use TLS, and encrypt data at rest and sensitive fields.
  • Keep credentials in secrets management, audit activity, and validate input to prevent NoSQL injection.

MongoDB Operations Examples

Each example shows the code, the output it produces and what each part does.

Example 1

Creating application users with least privilege

MongoDB Shell
use shop
db.createUser({ user: 'shopApi', pwd: 'use-a-long-random-secret', roles: [{ role: 'readWrite', db: 'shop' }] })
db.createUser({ user: 'reporting', pwd: 'another-long-random-secret', roles: [{ role: 'read', db: 'shop' }] })
db.getUsers().users.map(u => `${u.user}: ${u.roles.map(r => r.role + '@' + r.db).join(', ')}`)
Output
switched to db shop
{ ok: 1 }
{ ok: 1 }
[ 'reporting: read@shop', 'shopApi: readWrite@shop' ]
Line-by-line explanation
  1. Line 2The API can read and write the shop database, and nothing else.
  2. Line 3The reporting tool can only read; it can't change or delete anything.
  3. Lines 2 to 3In real use, generate long random passwords and enter them with passwordPrompt().
  4. Line 4getUsers lists users and their roles in the current database.
Example 2

What a read-only user can't do

MongoDB Shell
// connected as reporting
db.runCommand({ connectionStatus: 1 }).authInfo.authenticatedUsers
db.orders.countDocuments()
try {
  db.orders.deleteMany({})
} catch (e) {
  print(`${e.codeName}: ${e.message.split(' on ')[0]}`)
}
Output
[ { user: 'reporting', db: 'shop' } ]
1204
Unauthorized: not authorized
Line-by-line explanation
  1. Line 2connectionStatus shows which user this session is authenticated as.
  2. Line 3Reading is allowed by the read role.
  3. Line 5Deleting isn't, so even a mistaken or malicious command can't remove data.
  4. Line 7Error code name Unauthorized means the role lacks that action.
Example 3

A custom role and NoSQL injection

MongoDB Shell
use shop
db.createRole({
  role: 'orderStatusUpdater',
  privileges: [{ resource: { db: 'shop', collection: 'orders' }, actions: ['find', 'update'] }],
  roles: []
})
db.createUser({ user: 'warehouse', pwd: 'long-random-secret', roles: ['orderStatusUpdater'] })

const userInput = { $ne: null }
db.accounts.insertMany([{ email: 'ada@example.com' }, { email: 'ben@example.com' }]).acknowledged
db.accounts.find({ email: userInput }).toArray().length
db.accounts.find({ email: String(userInput) }).toArray().length
Output
switched to db shop
{ ok: 1 }
{ ok: 1 }
true
2
0
Line-by-line explanation
  1. Lines 2 to 6A custom role grants only find and update on the orders collection.
  2. Line 7The warehouse system can mark orders as shipped, but can't touch customers or delete anything.
  3. Line 9Imagine this object arrived in a JSON request body instead of an email string.
  4. Line 11Used directly, the operator matches every account: NoSQL injection.
  5. Line 12Forcing the value to a string, or validating its type first, makes it harmless.

Common MongoDB Operations Mistakes

Leaving authentication off

Don't
mongod --bind_ip_all                    # no auth, listening on every interface: open to the internet
Do
mongod --auth --bind_ip 127.0.0.1,10.0.1.12 --tlsMode requireTLS ...

Applications using an admin account

Don't
mongodb://root:password@db1:27017/?authSource=admin     // a bug or leak exposes every database
Do
mongodb://shopApi:...@db1:27017/shop?authSource=shop    // readWrite on shop only

Passwords in shell history and source code

Don't
db.createUser({ user: 'app', pwd: 'Summer2026!', roles: ['readWrite'] })   // saved in history, easy to guess
Do
db.createUser({ user: 'app', pwd: passwordPrompt(), roles: [{ role: 'readWrite', db: 'shop' }] })

MongoDB Operations in Real Projects

Security in production deployments

Teams run MongoDB on private networks or Atlas with IP access lists or private endpoints, require TLS, and store credentials in AWS Secrets Manager, Vault or similar tools. Each service has its own database user with a narrow role, credentials rotate automatically, and audit logs feed security monitoring. Sensitive fields such as health data or identity numbers use client-side field level encryption.

MongoDB Operations FAQ

How do I enable authentication in MongoDB?

Create an administrative user first, then set security.authorization: enabled in mongod.conf, or start mongod with --auth, and restart the server. From then on, clients must log in, and they can only do what their roles allow. In the official Docker image, setting MONGO_INITDB_ROOT_USERNAME and MONGO_INITDB_ROOT_PASSWORD creates a root user and enables authentication for you.

How do I change a MongoDB user's password?

Switch to the database where the user was created and run db.changeUserPassword('shopApp', passwordPrompt()). db.updateUser can change the password and roles together. Update the connection string in your application's secret store at the same time, or the app will fail to authenticate on its next connection.

How do I list the users in a MongoDB database?

Run db.getUsers() in the database where the users were created, or show users in mongosh. Each entry shows the user's name, database and roles, never the password. To see users from every database, query db.getSiblingDB('admin').system.users as an administrator.

What does authSource mean in a MongoDB connection string?

authSource names the database where the user was created, which is where MongoDB checks the credentials. If you created the user in admin but connect to the shop database, add ?authSource=admin, otherwise authentication fails even with the right password. When it's omitted, the database in the connection string's path is used, or admin if there isn't one.

Is MongoDB secure by default?

Modern versions bind to localhost only by default, so a fresh server isn't reachable from other machines. Access control, however, is off until you enable it, which is why exposing a server on a public address without authentication has led to many data leaks. Enable authentication, use TLS, restrict network access, and give each application a user with only the roles it needs.

MongoDB Operations Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Complete the role so the app can read and write the shop database.

db.createUser({ user: 'app', pwd: passwordPrompt(), roles: [{ role: '', db: 'shop' }] })
Multiple choiceeasy

What does least privilege mean?

Multiple choicemedium

Which built-in role can read but not change data?

Multiple choicemedium

Why use passwordPrompt() when creating users?

Multiple choicehard

How does NoSQL injection happen?

MongoDB Operations Quiz

5 questions to check what you learned.

  1. What should be enabled so every client must prove who it is?

    • Sharding
    • Authentication (access control)
    • Replication
    • Profiling
  2. What does TLS protect?

    • Data travelling over the network between clients and servers
    • Data on disk
    • Passwords in code
    • Indexes
  3. What's a custom role used for?

    • Granting exactly the actions needed on specific resources
    • Renaming users
    • Creating databases
    • Backups only
  4. Who should have the root role?

    • Every application
    • Almost nobody; it's for emergencies
    • Reporting tools
    • All developers
  5. What does client-side field level encryption add?

    • Faster queries
    • Automatic backups
    • Shorter passwords
    • Sensitive fields are encrypted by the application, so even database administrators can't read them