Performance & Security

MongoDB Intermediate/Advanced — Performance & Security
MongoDB Intermediate/Advanced
Chapter 7 · Performance & Security

🛡️ Performance & Security

This chapter has two halves that both matter before a MongoDB deployment is genuinely production-ready: revisiting mongodb1-7's index strategy with everything this course has since added (aggregation pipelines, sharding), and securing the deployment itself — authentication, authorization, and the connection string that ties an application to it.

Index Strategy, Revisited

mongodb1-7 introduced the Equality-Sort-Range (ESR) rule for ordering compound index fields, and reading explain() output to check whether a query used an index at all. Both apply just as much to this course's additions:

  • An aggregation's $match stage benefits from an index exactly like a find() filter does — which is the concrete reason Chapter 1 and Chapter 2 both stressed putting $match first.
  • A $sort stage can also use an index (avoiding an expensive in-memory sort) if it comes right after a $match that already narrowed things down using the same compound index, following the same ESR ordering.
  • On a sharded collection (Chapter 6), the shard key is implicitly part of how data is located — a query including the shard key can be routed to one shard and use a local index on that shard, while a query missing it pays the scatter-gather cost regardless of indexing.

The Database Profiler

Where explain() checks one query you already suspect is slow, the profiler watches everything and records slow operations automatically — the tool for finding problems you didn't already know to look for.

// Log any operation slower than 100ms into the system.profile collection db.setProfilingLevel(1, { slowms: 100 }) // Review what got logged, most recent first db.system.profile.find().sort({ ts: -1 }).limit(10)

Profiling level 1 logs only slow operations (the practical default); level 2 logs everything, which is useful briefly while debugging but far too much overhead to leave on in production. Once a slow query is found in system.profile, the same explain() workflow from mongodb1-7 diagnoses exactly why.

Authentication & Authorization

MongoDB doesn't require authentication by default on an out-of-the-box install — enabling it is a deliberate, necessary step, not something to assume already happened.

// mongod.conf security: authorization: enabled

Once enabled, every connection needs a user with specific granted roles — MongoDB's authorization model, built around the same least-privilege idea as choosing which SQL grants an application account actually needs.

Built-in RoleGrants
readRead-only access to a specific database
readWriteRead and write access to a specific database
dbAdminAdministrative tasks (indexes, stats) on a specific database, no data read/write
clusterAdminCluster-wide administrative actions (replica set/sharding config)
rootSuperuser — every privilege on every database; reserved for true administrators, never an application

Creating a Scoped Application User

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

This user can read and write the shop database only — it has no ability to touch any other database, drop the database, or perform cluster administration, even if the application's own code somehow tried to.

An Overly Broad Role vs. a Scoped One

root, or readWriteAnyDatabase

Convenient during early development, but means a single compromised application credential (a leaked connection string, an injection bug) grants an attacker access to every database on the server, not just this one application's.

readWrite Scoped to One Database

The same compromised credential now only exposes the one database that application was ever supposed to touch — the blast radius of a leak is contained by design, not by luck.

Connection String Security

A MongoDB connection string routinely contains a username and password directly in its text: mongodb+srv://user:password@cluster.example.net/mydb. Treat it exactly like any other credential — never commit one into source control, matching the CI/CD Pipelines course's core rule that a committed credential is compromised forever the moment it's pushed, even if the commit is later removed. Load it from an environment variable or secrets manager instead, the same pattern node2-6 covered for configuration generally.

// Node.js — read from the environment, never hardcode const client = new MongoClient(process.env.MONGODB_URI);

The connection string is also where TLS gets enabled for encryption in transit (tls=true), and where mongodb+srv:// (versus plain mongodb://) resolves the cluster's real hosts via DNS automatically — worth knowing what these options mean rather than copy-pasting a string a hosting provider generated.

Do

Enable authorization, create per-application users scoped to exactly the database(s) they need, load connection strings from environment variables or a secrets manager, use TLS in transit.

Don't

Run with authorization disabled, share one root credential across every application, commit a connection string to a repository, or expose a database port directly to the public internet.

💻 Coding Challenges

Challenge 1: Diagnose a Profiler Entry

The profiler logs a slow find({ email: "..." }) query on a 2-million-document users collection with no matching index. Using mongodb1-7's tools, what's the fix, and what would explain() have shown before and after?

Goal: Practice connecting a profiler finding back to the indexing chapter's diagnostic workflow.

→ Solution

Challenge 2: Design Least-Privilege Roles

A company has a shop app that needs full read/write on a shop database, and a separate nightly reporting job that only ever reads from it. Design the createUser() roles for each.

Goal: Practice applying least privilege to two genuinely different access needs sharing the same database, rather than granting both the same broad role.

→ Solution

Challenge 3: Find the Security Mistake

A developer commits a file containing const uri = "mongodb+srv://admin:Sup3rSecret@prod-cluster.example.net/shop"; directly into the application's Git repository. Explain everything wrong with this, and how it should be fixed.

Goal: Practice spotting a hardcoded, over-privileged, committed credential as three separate, stacking mistakes — not just one.

→ Solution

⚠️ Gotcha: An Open, Unauthenticated MongoDB Was a Real, Repeated Ransomware Target

This isn't a hypothetical risk — a well-known wave of ransomware attacks specifically targeted MongoDB instances left exposed to the public internet with authentication disabled, scanning for open 27017 ports, wiping the data, and leaving a ransom note in its place. Modern MongoDB versions bind to localhost by default specifically to make an accidental public exposure harder, but that default can still be overridden — treat "authorization enabled" and "not reachable from the open internet" as two separate requirements, not one, since either one alone still leaves a real door open.

🎯 What's Next

The final chapter is the Capstone: Designing a Real Application's Data Model — combining schema design, indexing, aggregation, and driver code from every chapter in this course into one small, real application built from scratch.