MongoDB Security
MongoDB security is layered — network isolation, encryption, authentication, authorization, and auditing. Skipping any layer exposes your data. Never run a production database with no authentication on a public IP.
Defense in depth for MongoDB
MongoDB security is layered — network isolation, encryption, authentication, authorization, and auditing. Skipping any layer exposes your data. Never run a production database with no authentication on a public IP.
Real-life example: Securing MongoDB is like securing a bank — fence (firewall), locked doors (auth), ID badges (RBAC), cameras (audit logs), and armored trucks (TLS encryption).
Enable access control and authentication
Start mongod with --auth or enable authorization in the config file. Create an admin user first, then application-specific users with least privilege.
SCRAM-SHA-256 is the default authentication mechanism. x.509 certificates work for internal cluster member auth.
Real-life example: Creating separate database users is giving each employee a key card that opens only their floor — the intern cannot enter the vault.
// Connect without auth on first setup, create admin
use admin
db.createUser({
user: "adminUser",
pwd: "Very-Strong-Random-Password-123!",
roles: [{ role: "userAdminAnyDatabase", db: "admin" }]
})
// Application user — readWrite only on rishtaara database
use rishtaara
db.createUser({
user: "rishtaara_app",
pwd: process.env.MONGO_APP_PASSWORD,
roles: [{ role: "readWrite", db: "rishtaara" }]
})
// Read-only user for reporting dashboards
db.createUser({
user: "rishtaara_readonly",
pwd: process.env.MONGO_READONLY_PASSWORD,
roles: [{ role: "read", db: "rishtaara" }]
})Role-based access control (RBAC)
- Built-in roles: read, readWrite, dbAdmin, userAdmin, clusterAdmin, root.
- Custom roles combine specific privileges: { resource: { db: "rishtaara", collection: "orders" }, actions: ["find", "insert"] }.
- Principle of least privilege — your API server gets readWrite on its database, not root.
- Atlas IAM integration and LDAP/SAML for enterprise single sign-on.
use rishtaara
db.createRole({
role: "logWriter",
privileges: [
{
resource: { db: "rishtaara", collection: "logs" },
actions: ["insert"]
}
],
roles: []
})
db.createUser({
user: "log_service",
pwd: "service-password",
roles: [{ role: "logWriter", db: "rishtaara" }]
})TLS, connection strings, and environment variables
Always use TLS in production. Atlas enforces TLS by default. Self-hosted clusters need certificates configured on mongod and in the connection string (?tls=true).
Store credentials in environment variables — never commit connection strings to Git. Use .env locally and secret managers (AWS Secrets Manager, Vault) in production.
Real-life example: Putting passwords in source code is writing your house key under the doormat — anyone who clones the repo gets in.
# .env — never commit this file
MONGODB_URI=mongodb+srv://rishtaara_app:<PASSWORD>@cluster0.xxxxx.mongodb.net/rishtaara?retryWrites=true&w=majority
# Node.js
await mongoose.connect(process.env.MONGODB_URI);NoSQL injection — validate all input
NoSQL injection happens when user input becomes part of a query object without sanitization. An attacker sends { "email": { "$gt": "" } } instead of a string and bypasses login checks.
Real-life example: NoSQL injection is tricking the receptionist with a fake ID format — 'anyone whose name is greater than empty string' matches every record.
// VULNERABLE — user body merged into query
app.post("/login", async (req, res) => {
const user = await User.findOne(req.body); // attacker sends { email: { $gt: "" } }
});
// SAFE — explicit typed fields only
app.post("/login", async (req, res) => {
const email = String(req.body.email ?? "").trim().toLowerCase();
const password = String(req.body.password ?? "");
if (!email || !password) return res.status(400).json({ error: "Missing fields" });
const user = await User.findOne({ email }).select("+passwordHash");
// verify password with bcrypt...
});