MongoDB Atlas Hardening Guide
Database-as-a-Service security hardening for MongoDB Atlas network access, authentication, and encryption
Overview
MongoDB Atlas is the leading cloud database platform, serving millions of developers with fully managed MongoDB deployments across AWS, Azure, and Google Cloud. As a critical data store for applications, Atlas security configurations directly impact data protection. By default, all access is blocked and must be explicitly enabled, but misconfigurations can expose databases to unauthorized access.
Intended Audience
- Security engineers managing database infrastructure
- Database administrators configuring Atlas clusters
- GRC professionals assessing data security
- DevOps engineers implementing secure deployments
How to Use This Guide
- L1 (Crawl): Essential controls for all organizations
- L2 (Walk): Enhanced controls for security-sensitive environments
- L3 (Run): Strictest controls for regulated industries
Scope
This guide covers MongoDB Atlas security configurations including network access, authentication, encryption, and monitoring. Self-managed MongoDB deployments are covered in a separate guide.
Table of Contents
- Network Security
- Authentication & Access
- Encryption & Data Protection
- Monitoring & Auditing
- Compliance Quick Reference
1. Network Security
1.1 Configure IP Access List
Profile Level: L1 (Crawl)
| Framework | Control |
|---|---|
| CIS Controls | 13.5 |
| NIST 800-53 | SC-7 |
Description
Configure IP access lists to restrict which IP addresses can connect to your Atlas clusters. By default, all access is blocked.
Rationale
Why This Matters:
- Default-deny ensures no unauthorized network access — Atlas blocks everything until you explicitly open it, so every entry on this list is a deliberate exposure decision
- IP allowlisting limits exposure to known addresses, so a stolen database credential is unusable from anywhere but your own network egress points
- A
0.0.0.0/0entry, however temporary, publishes the cluster to the entire internet and turns a credential-strength problem into an internet-facing exposure — this is the single most common Atlas misconfiguration - Expiring entries for developer access prevent temporary home or coffee-shop IPs from becoming permanent standing access
Attack Prevented: Internet-wide database exposure, credential reuse from attacker infrastructure, automated internet-scan discovery, standing access via stale temporary entries
ClickOps Implementation
Step 1: Access Network Configuration
- Navigate to: MongoDB Atlas → Project → Network Access
- Review current IP access list
Step 2: Configure IP Access
- Click Add IP Address
- Configure allowed IPs:
- Development: Individual developer IPs (temporary)
- Production: Application server IPs/CIDR ranges only
- NEVER: 0.0.0.0/0 (allows any IP)
- Add description for each entry
- Set expiration for temporary access
Best Practices:
| Environment | Recommended Configuration |
|---|---|
| Development | Individual IPs with expiration |
| Staging | Application server IPs only |
| Production | Smallest CIDR possible, VPC peering preferred |
Do not pin literal Atlas IP addresses in your own outbound firewall rules. MongoDB began rotating the public IPv4 addresses of AWS dedicated clusters on 2025-01-21. Any egress allowlist, security group, or connection string built on hard-coded Atlas IPs will break as addresses rotate. Connect with the
mongodb+srv://connection string, which resolves current endpoints automatically, and allowlist by hostname or use private connectivity (control 1.2) where your firewall supports it.
Time to Complete: ~15 minutes
Code Pack: Terraform
# -----------------------------------------------------------------------------
# 1.1 IP Access List Entries
# Each entry restricts database connections to an approved CIDR range.
# NEVER include 0.0.0.0/0 -- this opens databases to the entire internet.
# -----------------------------------------------------------------------------
resource "mongodbatlas_project_ip_access_list" "allowed" {
for_each = { for idx, entry in var.allowed_cidr_blocks : idx => entry }
project_id = var.atlas_project_id
cidr_block = each.value.cidr_block
comment = each.value.comment
}
Code Pack: API Script
# Retrieve all IP access list entries for the project
ACCESS_LIST=$(atlas_get "/groups/${ATLAS_PROJECT_ID}/accessList") || {
fail "1.1 Failed to retrieve IP access list"
increment_failed
summary
exit 1
}
TOTAL_ENTRIES=$(echo "${ACCESS_LIST}" | jq '.totalCount // 0')
info "1.1 Found ${TOTAL_ENTRIES} IP access list entries"
# Check for open access (0.0.0.0/0) -- critical finding
OPEN_ENTRIES=$(echo "${ACCESS_LIST}" | jq -r '
.results[]
| select(.cidrBlock == "0.0.0.0/0" or .cidrBlock == "::/0")
| .cidrBlock
' 2>/dev/null || true)
if [ -n "${OPEN_ENTRIES}" ]; then
fail "1.1 CRITICAL: Open access entry detected -- databases exposed to entire internet"
echo "${OPEN_ENTRIES}" | while read -r cidr; do
fail " - ${cidr}"
done
increment_failed
else
pass "1.1 No open access entries (0.0.0.0/0) found"
increment_applied
fi
# Report overly broad CIDR blocks (larger than /16)
BROAD_ENTRIES=$(echo "${ACCESS_LIST}" | jq -r '
.results[]
| select(.cidrBlock != null)
| select(
(.cidrBlock | split("/") | .[1] | tonumber) < 16
)
| "\(.cidrBlock) (\(.comment // "no comment"))"
' 2>/dev/null || true)
if [ -n "${BROAD_ENTRIES}" ]; then
warn "1.1 Overly broad CIDR blocks detected (wider than /16):"
echo "${BROAD_ENTRIES}" | while read -r entry; do
warn " - ${entry}"
done
fi
# Check for entries with no comment (poor documentation)
UNCOMMENTED=$(echo "${ACCESS_LIST}" | jq '[.results[] | select(.comment == null or .comment == "")] | length' 2>/dev/null || echo "0")
if [ "${UNCOMMENTED}" -gt 0 ]; then
warn "1.1 ${UNCOMMENTED} access list entries have no comment (add descriptions for audit trail)"
fi
# Report temporary entries that may have expired or are about to
TEMP_ENTRIES=$(echo "${ACCESS_LIST}" | jq -r '
.results[]
| select(.deleteAfterDate != null)
| "\(.cidrBlock) expires \(.deleteAfterDate)"
' 2>/dev/null || true)
if [ -n "${TEMP_ENTRIES}" ]; then
info "1.1 Temporary access list entries:"
echo "${TEMP_ENTRIES}" | while read -r entry; do
info " - ${entry}"
done
fi
Code Pack: Sigma Detection Rule
detection:
selection:
eventTypeName:
- 'ACCESS_LIST_ENTRY_ADDED'
- 'PROJECT_IP_ACCESS_LIST_ENTRY_ADDED'
filter_open_access:
cidrBlock|contains:
- '0.0.0.0/0'
- '::/0'
condition: selection and filter_open_access
fields:
- username
- remoteAddress
- cidrBlock
- groupId
- created
1.2 Configure VPC Peering or Private Endpoints
Profile Level: L2 (Walk)
| Framework | Control |
|---|---|
| CIS Controls | 12.1 |
| NIST 800-53 | SC-7 |
Description
Configure private connectivity via VPC peering or private endpoints to eliminate public internet exposure.
Rationale
Why This Matters:
- Private endpoints eliminate public internet exposure entirely — the cluster has no reachable public listener to attack, rather than a public listener guarded by an allowlist
- Traffic stays within the cloud provider network, removing exposure to internet-path interception and to the operational fragility of maintaining IP allowlists across a changing fleet
- More secure than IP allowlisting alone: an allowlist still fails open if someone adds a broad CIDR, while a private-only cluster cannot be reached from an attacker’s network no matter what the list says
- Private connectivity also survives the Atlas public-IP rotation described in control 1.1, removing an entire class of brittle firewall configuration
Attack Prevented: Internet-facing exposure, network eavesdropping, allowlist misconfiguration, credential use from attacker-controlled networks
Prerequisites
- A Dedicated cluster (M10 or higher). Private endpoints and VPC peering are not available on Free or Flex clusters — see Appendix A
- AWS VPC, Azure VNet, or GCP VPC configured
ClickOps Implementation
Step 1: Configure VPC Peering
- Navigate to: Network Access → Peering
- Click Add Peering Connection
- Select cloud provider and region
- Enter VPC/VNet details:
- VPC ID
- CIDR block
- Account/Project ID
- Accept peering from your cloud provider console
Step 2: Configure Private Endpoints (Recommended)
- Navigate to: Network Access → Private Endpoint
- Click Add Private Endpoint
- Select cloud provider and region
- Follow provider-specific instructions:
- AWS: Create VPC endpoint
- Azure: Create private endpoint
- GCP: Create private service connect
Step 3: Update IP Access List
- Private endpoints are automatically added
- Remove public IP entries if no longer needed
- Verify connectivity through private endpoint
Connection rate limit (announced November 2025): M10 and M20 clusters cap new connections at 15 per second per node. Applications that open connections aggressively — serverless functions without connection reuse, or a deployment that restarts every pod at once — will be throttled, and the resulting failures look like a network or endpoint problem rather than a rate limit. Use pooled connections and staggered restarts, and size up if your legitimate connection rate genuinely exceeds the cap.
Time to Complete: ~1 hour
1.3 Enforce Atlas Resource Policies
Profile Level: L2 (Walk)
| Framework | Control |
|---|---|
| CIS Controls | 4.1 |
| NIST 800-53 | CM-2, CM-6, CM-7 |
Description
Use Atlas Resource Policies — generally available, Cedar-based, and set at the organization level — to make the hardening decisions in this guide structurally unbreakable across every project, so that a project owner cannot open a cluster to the internet or stand one up without auditing and customer-managed keys.
Rationale
Why This Matters:
- Every other control in this guide is a setting a sufficiently privileged project user can undo; a resource policy is an organization-level guardrail that they cannot, which converts hardening from a recurring audit chore into an enforced invariant
- The highest-severity Atlas incidents come from a single project drifting — one
0.0.0.0/0entry, one cluster created without auditing, one deployment in an unapproved region — and policies stop the drift at creation time rather than at the next review - Policies enforce controls across projects that do not exist yet, so newly created projects inherit the posture instead of starting unhardened and waiting for someone to notice
- Non-compliance is queryable, so you get a concrete remediation list rather than an assertion that everything is fine
Attack Prevented: Configuration drift, unauthorized internet exposure, unaudited clusters, data residency violations, privilege misuse by project-level administrators
Prerequisites
- Organization Owner role (policies are managed at the organization level)
ClickOps Implementation
Step 1: Review Available Policy Types
Atlas Resource Policies can enforce, among others:
| Policy | Effect |
|---|---|
Prohibit 0.0.0.0/0 CIDR |
Blocks the internet-open IP access-list entry outright (control 1.1) |
| Require empty IP access list | Forces private-connectivity-only clusters (control 1.2) |
| Block IP access-list modification | Prevents project users from changing network exposure at all |
| Restrict cloud providers | Limits deployments to approved providers |
| Restrict regions | Enforces data-residency requirements |
| Require database auditing | Makes auditing non-optional on every cluster (control 4.1) |
| Require customer-managed keys | Makes CMK encryption non-optional (control 3.2) |
| Minimum TLS version and cipher suites | Prevents downgrade to weaker transport settings |
| Maintenance window constraints | Keeps disruptive maintenance inside approved windows |
| Cluster topology and tier limits | Constrains what shapes and sizes can be deployed |
Step 2: Author and Apply Policies
- Navigate to: Organization → Settings → Resource Policies
- Create a policy using the Cedar policy language
- Scope it to the organization or to specific projects
- Apply, then confirm existing projects surface as compliant or non-compliant
Step 3: Monitor Non-Compliance
- Query the Administration API endpoint
/orgs/{ORG-ID}/nonCompliantResourcesfor resources that violate an active policy - Wire that query into your existing configuration-drift alerting rather than checking it by hand
- Remediate or grant a documented exception for each result
Reference: Atlas Resource Policies
Validation & Testing
- In a test project, attempt to add a
0.0.0.0/0IP access-list entry and confirm the policy refuses it - Attempt to create a cluster without auditing enabled and confirm creation is blocked
- Confirm
/orgs/{ORG-ID}/nonCompliantResourcesreturns an empty result set for your production organization
2. Authentication & Access
2.1 Configure Database Users with Least Privilege
Profile Level: L1 (Crawl)
| Framework | Control |
|---|---|
| CIS Controls | 5.4 |
| NIST 800-53 | AC-6 |
Description
Create database users with role-based access control (RBAC) following the principle of least privilege.
Rationale
Why This Matters:
- Limits blast radius of compromised credentials — a leaked application credential scoped to
readon one database cannot be used to enumerate, modify, or drop the rest of the deployment - Supports compliance requirements by producing defensible, per-purpose access grants rather than a small number of shared all-powerful users
- Enables audit of access patterns: when each application has its own database user, audit logs attribute every query to a specific workload instead of to a shared identity
atlasAdminand the*AnyDatabaseroles are effectively deployment-wide access — every additional holder is another credential whose compromise is a total compromise
Attack Prevented: Credential compromise escalating to full-deployment access, lateral movement between databases, unauthorized data modification or deletion, repudiation through shared credentials
ClickOps Implementation
Step 1: Access Database Users
- Navigate to: Database Access → Database Users
- Review existing users
Step 2: Create Least Privilege Users
- Click Add New Database User
- Configure authentication:
- SCRAM: Password-based (most common)
- X.509 Certificate: Certificate-based (recommended for machine-to-machine — see control 2.3)
- AWS IAM: For AWS workloads
- OIDC / OAuth 2.0 Workforce or Workload Identity Federation: Federated authentication — the documented modern replacement for LDAP (see the callout below)
- LDAP: Deprecated — see the callout below
- Configure privileges:
- Built-in Role: Select appropriate role
- Custom Role: Create granular permissions
- Restrict to specific database if possible
Recommended Roles:
| Use Case | Recommended Role |
|---|---|
| Application read | readAnyDatabase or read on specific DB |
| Application write | readWriteAnyDatabase or readWrite on specific DB |
| Admin operations | dbAdmin on specific DB |
| Full admin | atlasAdmin (limit to 1-2 users) |
Step 3: Create Separate Service Accounts
- Create dedicated users for each application
- Avoid shared credentials
- Document user purpose
Step 4: Plan the Move Off LDAP
LDAP is deprecated. MongoDB’s documentation states: “Starting with MongoDB 8.0, LDAP authentication and authorization are deprecated.” Deprecated means still operational for the lifetime of MongoDB 8, with removal in a future major release — not immediately broken. Treat it as a migration you schedule rather than an emergency, but do schedule it: the removal release will arrive, and LDAP-authenticated workloads will stop connecting when it does.
The documented replacement is OIDC / OAuth 2.0 federated authentication, in two forms:
| Mode | Use for |
|---|---|
| Workforce Identity Federation | Human users authenticating to databases through your corporate IdP |
| Workload Identity Federation | Applications and services authenticating with short-lived tokens instead of stored secrets |
- Inventory every database user and workload currently authenticating via LDAP
- Choose Workforce or Workload federation per identity type
- Configure the OIDC provider in Database Access → Federated Authentication
- Migrate and validate one workload at a time, then remove the LDAP configuration
OIDC and LDAP authorization cannot coexist. You cannot run OIDC authentication alongside LDAP authorization on the same deployment, so plan a clean cutover for authorization rather than a gradual dual-run.
References: Set up LDAPS · Configure database authentication
Time to Complete: ~30 minutes
Code Pack: Terraform
# -----------------------------------------------------------------------------
# 2.1 Database Users with Scoped Roles
# Each user receives only the minimum permissions needed. Roles are scoped
# to specific databases rather than granted project-wide. Avoid atlasAdmin
# and readWriteAnyDatabase unless absolutely required and documented.
# -----------------------------------------------------------------------------
resource "mongodbatlas_database_user" "users" {
for_each = { for idx, user in var.database_users : user.username => user }
project_id = var.atlas_project_id
username = each.value.username
password = each.value.password
auth_database_name = each.value.auth_database
dynamic "roles" {
for_each = each.value.roles
content {
role_name = roles.value.role_name
database_name = roles.value.database_name
collection_name = roles.value.collection_name
}
}
dynamic "scopes" {
for_each = each.value.scopes
content {
name = scopes.value.name
type = scopes.value.type
}
}
}
Code Pack: API Script
# Retrieve all database users for the project
DB_USERS=$(atlas_get "/groups/${ATLAS_PROJECT_ID}/databaseUsers") || {
fail "2.1 Failed to retrieve database users"
increment_failed
summary
exit 1
}
TOTAL_USERS=$(echo "${DB_USERS}" | jq '.totalCount // 0')
info "2.1 Found ${TOTAL_USERS} database users"
# Check for users with atlasAdmin role on all databases (overly permissive)
ADMIN_USERS=$(echo "${DB_USERS}" | jq -r '
.results[]
| select(
.roles[]
| select(.roleName == "atlasAdmin" and (.databaseName == "admin" or .databaseName == ""))
)
| .username
' 2>/dev/null | sort -u || true)
if [ -n "${ADMIN_USERS}" ]; then
fail "2.1 Users with atlasAdmin role detected (overly permissive):"
echo "${ADMIN_USERS}" | while read -r user; do
fail " - ${user}"
done
increment_failed
else
pass "2.1 No users with unrestricted atlasAdmin role"
increment_applied
fi
# Check for users with readWriteAnyDatabase (broad access)
BROAD_USERS=$(echo "${DB_USERS}" | jq -r '
.results[]
| select(
.roles[]
| select(.roleName == "readWriteAnyDatabase")
)
| .username
' 2>/dev/null | sort -u || true)
if [ -n "${BROAD_USERS}" ]; then
warn "2.1 Users with readWriteAnyDatabase role (consider scoping to specific databases):"
echo "${BROAD_USERS}" | while read -r user; do
warn " - ${user}"
done
fi
# Check for users authenticating with SCRAM (password) vs X.509 or LDAP
SCRAM_USERS=$(echo "${DB_USERS}" | jq '[.results[] | select(.databaseName == "admin")] | length' 2>/dev/null || echo "0")
X509_USERS=$(echo "${DB_USERS}" | jq '[.results[] | select(.databaseName == "$external" and .x509Type != "NONE")] | length' 2>/dev/null || echo "0")
LDAP_USERS=$(echo "${DB_USERS}" | jq '[.results[] | select(.ldapAuthType != null and .ldapAuthType != "NONE")] | length' 2>/dev/null || echo "0")
info "2.1 Authentication breakdown:"
info " - SCRAM (password): ${SCRAM_USERS}"
info " - X.509 certificate: ${X509_USERS}"
info " - LDAP: ${LDAP_USERS}"
if [ "${SCRAM_USERS}" -gt 0 ] && should_apply 2 2>/dev/null; then
warn "2.1 L2 recommends migrating SCRAM users to X.509 or LDAP authentication"
fi
# Check for users with no role scoping (roles on all clusters)
UNSCOPED_USERS=$(echo "${DB_USERS}" | jq -r '
.results[]
| select(.scopes == null or (.scopes | length) == 0)
| .username
' 2>/dev/null || true)
if [ -n "${UNSCOPED_USERS}" ]; then
warn "2.1 Users with no cluster scope (access to all clusters in project):"
echo "${UNSCOPED_USERS}" | while read -r user; do
warn " - ${user}"
done
fi
2.2 Enable Multi-Factor Authentication for Atlas Console
Profile Level: L1 (Crawl)
| Framework | Control |
|---|---|
| CIS Controls | 6.5 |
| NIST 800-53 | IA-2(1) |
Description
Require MFA for all users accessing the MongoDB Atlas console.
Rationale
Why This Matters:
- The Atlas console controls network access lists, database users, encryption keys, and cluster configuration — a single compromised admin login can expose or destroy all data
- Passwords alone are vulnerable to phishing, credential stuffing, and reuse from prior breaches; MFA adds a second factor an attacker cannot obtain with the password alone
- MongoDB’s own December 2023 corporate breach began with a phishing attack, underscoring that console and identity compromise is a realistic threat
- Security keys and biometrics (FIDO2) are phishing-resistant — they will not release a credential to a lookalike domain, which is exactly the failure mode that defeats one-time codes
- Enrolling a second factor method protects against lockout: losing your only enrolled device on an MFA-required organization is a support ticket, not a login
Attack Prevented: Credential theft, phishing, credential stuffing, account takeover, real-time one-time-code relay
ClickOps Implementation
Step 1: Configure Organization MFA
- Navigate to: Organization → Settings → Require Multi-Factor Authentication
- Enable MFA requirement for all organization members
Step 2: Configure Personal MFA
- Each user: Account → Security → Multi-Factor Authentication
- Configure MFA methods in this order of preference:
| Method | Recommendation |
|---|---|
| FIDO2 security key or biometric | Preferred — phishing-resistant |
| Okta Verify | Strong; use where Okta is already your IdP |
| Authenticator app (TOTP) | Acceptable baseline |
| Weakest supported option; use only as a fallback | |
| SMS | Deprecated — see callout below |
- Enroll at least two methods per account so a lost device does not lock the user out
SMS is deprecated and closed to new registrations. As of 2025-03-12, MongoDB no longer accepts new SMS factor registrations. Existing SMS enrollments continue to function as a legacy method, but SMS is vulnerable to SIM-swap and interception and should be replaced. Migrate any account still relying on SMS to FIDO2 or an authenticator app.
Reference: Atlas multi-factor authentication
2.3 Configure X.509 Certificate Authentication
Profile Level: L2 (Walk)
| Framework | Control |
|---|---|
| CIS Controls | 6.5 |
| NIST 800-53 | IA-2 |
Description
Configure X.509 certificate authentication for stronger machine-to-machine authentication.
Rationale
Why This Matters:
- Certificate-based authentication removes static database passwords that leak through source code, config files, logs, and environment variables
- X.509 credentials are bound to a private key the client holds, making them far harder to phish or replay than a shared secret
- Certificates carry an explicit expiration, forcing periodic rotation and limiting the useful lifetime of a stolen credential
- Atlas-managed or self-managed CA issuance enables centralized revocation when a host or service is decommissioned or compromised
Attack Prevented: Credential leakage, password reuse, credential replay, orphaned-credential access
ClickOps Implementation
Step 1: Enable X.509 Authentication
- Navigate to: Database Access → Database Users
- Click Add New Database User
- Select Certificate authentication
- Choose:
- Atlas-managed: Atlas manages certificates
- Self-managed: You provide CA and certificates
Step 2: Configure Atlas-Managed X.509
- Download client certificate for your application
- Configure application connection string with certificate
- Rotate certificates before expiration
2.4 Configure Organization and Project Roles
Profile Level: L1 (Crawl)
| Framework | Control |
|---|---|
| CIS Controls | 5.4 |
| NIST 800-53 | AC-6(1) |
Description
Configure granular roles for Atlas console access at organization and project levels.
Rationale
Why This Matters:
- Granular org and project roles enforce least privilege so each user holds only the access their job requires, shrinking the blast radius of any compromised account
- Limiting Organization Owner assignments prevents broad, unchecked control over billing, clusters, and security settings
- Separating data-access administration from cluster management enforces separation of duties for sensitive operations
- Read-only roles let auditors and stakeholders review configuration without the ability to change it
Attack Prevented: Privilege escalation, lateral movement, insider misuse, excessive standing access
ClickOps Implementation
Step 1: Review Organization Roles
- Navigate to: Organization → Access Manager
- Review user assignments
- Available roles:
| Organization Role | Grants |
|---|---|
| Organization Owner | Full control of the organization, including resource policies and org settings — limit to 2-3 |
| Organization Project Creator | Can create new projects and becomes their owner — a broad grant, because a new project starts outside your existing review |
| Organization Billing Admin | Billing management |
| Organization Billing Viewer | Read-only billing |
| Organization Stream Processing Admin | Administers Atlas Stream Processing across the organization |
| Organization Member | Basic organization access |
| Organization Read Only | View only |
Step 2: Review Project Roles
- Navigate to: Project → Access Manager
- Assign project-specific roles:
| Project Role | Grants |
|---|---|
| Project Owner | Full control of the project |
| Project Cluster Manager | Create, modify, and manage clusters |
| Project Data Access Admin | Full database-user administration |
| Project Data Access Read/Write | Read and write to project data |
| Project Data Access Read Only | Read project data |
| Project Stream Processing Owner | Full control of stream processing instances |
| Project Read Only | View only |
Watch the Project Access Manager role. Project Access Manager can create both API keys and service accounts for the project. That makes it a privilege-escalation path: a holder who cannot directly perform an action can mint a programmatic credential with roles they choose and act through it, and the resulting activity is attributed to the credential rather than to them. Treat Project Access Manager as a privileged grant on par with Project Owner, review its holders on the same cadence, and audit newly created API keys and service accounts against the person who created them.
References: Atlas user roles
2.5 Harden Administration API Access
Profile Level: L2 (Walk)
| Framework | Control |
|---|---|
| CIS Controls | 5.4, 6.5 |
| NIST 800-53 | AC-3, IA-5 |
Description
Access the Atlas Administration API with service accounts using OAuth 2.0 client credentials and expiring tokens rather than legacy API keys, and require an IP access list for Administration API use at the organization level.
Rationale
Why This Matters:
- Legacy Atlas API keys authenticate with HTTP Digest and do not expire — a leaked key in a CI log, a git history, or a decommissioned developer’s password manager stays valid indefinitely until someone notices and revokes it
- Service accounts use the OAuth 2.0 client-credentials flow and issue expiring access tokens, so a captured token has a bounded useful lifetime and secret rotation is a documented, supported operation rather than a manual scramble
- The Administration API can create and delete clusters, edit the IP access list, manage database users, and change encryption settings — it is a control-plane credential, and control-plane compromise is broader than any single database credential
- Requiring an IP access list for Administration API calls means a stolen credential is unusable from the attacker’s own infrastructure, adding a network condition to a credential that otherwise works from anywhere
Attack Prevented: Control-plane takeover via leaked API credentials, indefinite validity of exposed secrets, API abuse from attacker-controlled networks, unauthorized cluster and access-list modification
ClickOps Implementation
Step 1: Migrate to Service Accounts
- Navigate to: Organization → Access Manager → Applications (service accounts) or the project-level equivalent
- Create a service account per integration, scoped with the minimum organization or project roles it needs
- Record the client ID and client secret in your secrets manager
- Repoint each automation at the OAuth 2.0 client-credentials flow to obtain short-lived access tokens
Step 2: Retire Legacy API Keys
- Inventory existing API keys at both organization and project level
- Confirm each has a documented owner and purpose; delete the rest
- Replace remaining keys with service accounts, then delete the keys
Step 3: Require an IP Access List for the API
- Navigate to: Organization → Settings
- Enable Require IP Access List for the Atlas Administration API
- Add the egress addresses of your CI/CD systems and administrative jump hosts to each credential’s access list
The IP access list gates API use, not credential creation. Enabling it does not stop someone from creating a new API key or service account — it constrains where credentials can be used from. Pair this control with the Project Access Manager review in control 2.4, which is the surface that governs credential creation.
Step 4: Rotate on a Schedule
- Document a rotation interval for each service-account secret
- Rotate immediately on any suspected exposure, and whenever a person with access to the secret leaves
Reference: Configure Atlas Administration API access
Validation & Testing
- Confirm no legacy API keys remain in Access Manager for production organizations
- Call the Administration API from an address outside the access list and confirm the request is refused
- Confirm access tokens issued to service accounts expire as expected
3. Encryption & Data Protection
3.1 Verify Default Encryption
Profile Level: L1 (Crawl)
| Framework | Control |
|---|---|
| CIS Controls | 3.11 |
| NIST 800-53 | SC-8, SC-28 |
Description
Verify that default encryption at rest and in transit is enabled (cannot be disabled in Atlas).
Rationale
Why This Matters:
- Encryption at rest (AES-256) protects data on disk, in backups, and in snapshots if the underlying storage media is stolen, mishandled, or improperly decommissioned
- Encryption in transit (TLS 1.2+) prevents interception or tampering of data as it travels between applications and the cluster
- Verifying these defaults provides documented evidence for SOC 2, PCI DSS, HIPAA, and ISO 27001 audits
- Confirming TLS is enforced ensures no client can negotiate an unencrypted or downgraded connection
Attack Prevented: Data theft at rest, network eavesdropping, man-in-the-middle, TLS downgrade
Atlas Default Security
| Feature | Default Setting | Can Disable? |
|---|---|---|
| Encryption at Rest (AES-256) | ✅ Enabled | ❌ No |
| Encryption in Transit (TLS 1.2+) | ✅ Enabled | ❌ No |
| TLS 1.3 Support | ✅ Available | N/A |
Validation
- Navigate to: Clusters → Select cluster → Security
- Verify encryption indicators show enabled
- Test connection requires TLS
3.2 Configure Customer Key Management (CMK)
Profile Level: L2 (Walk)
| Framework | Control |
|---|---|
| CIS Controls | 3.11 |
| NIST 800-53 | SC-12 |
Description
Configure customer-managed encryption keys for additional control over data encryption.
Rationale
Why This Matters:
- Provides customer control over encryption keys — you hold the key material in your own KMS, so MongoDB cannot decrypt your data without a key you continue to grant access to
- Revoking or disabling the key in your KMS renders the stored data unreadable, giving you a unilateral kill switch that does not depend on the provider acting on your behalf
- Supports compliance requirements (PCI DSS, HIPAA) that expect demonstrable customer control and separation of duties over encryption keys
- Enables key rotation policies on your own schedule, bounding how much data any single key version protects
Attack Prevented: Provider-side data exposure, unauthorized access to at-rest data after cluster or snapshot compromise, inability to revoke access to stored data, key-lifetime sprawl
Prerequisites
- A Dedicated cluster (M10 or higher). Customer key management is not available on Free or Flex clusters — see Appendix A
- Cloud provider KMS (AWS KMS, Azure Key Vault, GCP Cloud KMS)
ClickOps Implementation
Step 1: Configure Cloud Provider KMS
- Create KMS key in your cloud provider
- Configure key policy for Atlas access
- Note key ARN/ID
Step 2: Enable CMK in Atlas
- Navigate to: Project → Security → Encryption at Rest
- Click Configure Encryption at Rest
- Select cloud provider
- Enter KMS key details
- Configure role/credentials for Atlas access
- Enable encryption
Step 3: Verify CMK Configuration
- Check cluster shows CMK-encrypted
- Test key rotation capability
Time to Complete: ~1 hour
Code Pack: Terraform
# -----------------------------------------------------------------------------
# 3.2 Encryption at Rest with Customer-Managed Keys (AWS KMS)
# Enables customer-controlled encryption keys so the organization retains
# full control over the key lifecycle (rotation, revocation, audit).
# Atlas uses envelope encryption: the CMK wraps the data encryption key.
# -----------------------------------------------------------------------------
resource "mongodbatlas_encryption_at_rest" "cmk" {
project_id = var.atlas_project_id
aws_kms_config {
enabled = true
customer_master_key_id = var.aws_kms_key_id
region = var.aws_kms_region
access_key_id = var.aws_access_key_id
secret_access_key = var.aws_secret_access_key
role_id = var.aws_role_arn != "" ? var.aws_role_arn : null
}
}
3.3 Configure Client-Side Field Level Encryption
Profile Level: L3 (Run)
| Framework | Control |
|---|---|
| CIS Controls | 3.11 |
| NIST 800-53 | SC-28 |
Description
Configure Client-Side Field Level Encryption (CSFLE) to encrypt sensitive fields before they leave the application.
Rationale
Why This Matters:
- Encrypts PII and sensitive data at the field level before it ever leaves the application, so the plaintext never reaches the database server, its memory, its logs, or its backups
- Data remains encrypted in the database, which means a database administrator, a compromised cluster, or a stolen snapshot yields ciphertext rather than customer records
- Only clients holding the data encryption keys can decrypt, moving the trust boundary from “who can reach the database” to “who holds the key” — the strongest available separation between infrastructure access and data access
- Field-level scope lets you protect the handful of genuinely sensitive fields without paying the query and operational cost of encrypting everything
Attack Prevented: Insider access to sensitive fields by database or cloud administrators, plaintext exposure through snapshots and backups, data theft following full cluster compromise, sensitive data leakage into logs
Implementation
- Configure encryption schema defining fields to encrypt
- Generate data encryption keys
- Configure application driver with encryption settings
- Test encryption/decryption of sensitive fields
3.4 Enable Backup Compliance Policy
Profile Level: L3 (Run)
| Framework | Control |
|---|---|
| CIS Controls | 11.1, 11.3 |
| NIST 800-53 | CP-9, CP-10, SI-1 |
Description
Enable Backup Compliance Policy on Dedicated (M10+) clusters to make cloud backup snapshots immutable — once the policy is active, no role in your organization, including Organization Owner, can delete a snapshot, shorten its retention, or disable backup.
Rationale
Why This Matters:
- Every other backup control assumes the person holding admin rights is acting in good faith; ransomware operators and rogue administrators both begin by destroying backups, and a backup an administrator can delete is not a recovery guarantee
- Backup Compliance Policy removes that capability entirely — there is no role that can delete snapshots or reduce retention while the policy is active, so credential compromise at the highest privilege level still leaves your recovery points intact
- Disabling the policy requires a designated security or legal representative plus MongoDB Support approval (a process MongoDB hardened further in February 2026), which means an attacker cannot unwind it inside an incident window
- Project deletion is blocked until the snapshots expire, closing the obvious workaround of deleting the whole project to get rid of the backups
- This is the control that turns “we have backups” into a defensible recovery position for regulators and cyber-insurance underwriters
Attack Prevented: Ransomware destruction of recovery points, rogue-administrator backup deletion, retention tampering to hide activity, backup destruction via project deletion
Prerequisites
- A Dedicated cluster (M10 or higher) with cloud backup enabled
- A designated security or legal representative recorded in the policy — this contact is required to request that the policy ever be disabled
ClickOps Implementation
Step 1: Decide the Policy Before Enabling It
This is a one-way door by design. Once enabled, the policy cannot be disabled or weakened by anyone in your organization. Removing it requires the designated security/legal representative to go through MongoDB Support. Set retention periods you can live with — over-long retention becomes an unavoidable storage cost and an unavoidable data-retention obligation.
- Agree the minimum retention period and backup frequency with your legal, security, and finance stakeholders
- Identify the designated security or legal representative
Step 2: Enable the Policy
- Navigate to: Organization → Settings → Backup Compliance Policy
- Enter the designated security or legal representative’s details
- Configure the minimum backup schedule, retention periods, and point-in-time recovery window
- Enable the policy and confirm
Step 3: Verify Enforcement
- As an Organization Owner, attempt to delete a snapshot and confirm the operation is refused
- Attempt to reduce a retention period below the policy floor and confirm it is refused
- Confirm the policy applies to the projects and clusters you intended
References: Backup Compliance Policy
4. Monitoring & Auditing
4.1 Enable Database Auditing
Profile Level: L1 (Crawl)
| Framework | Control |
|---|---|
| CIS Controls | 8.2 |
| NIST 800-53 | AU-2 |
Description
Enable database auditing to log authentication attempts and data access.
Rationale
Why This Matters:
- Audit logs capture authentication attempts and data access, providing the forensic trail needed to detect and investigate unauthorized activity
- Without auditing, a breach or insider abuse can occur with no record of who accessed what data and when
- Exporting audit events to a SIEM or object storage enables real-time alerting and tamper-resistant long-term retention
- Comprehensive audit trails are required to demonstrate compliance with frameworks such as SOC 2, PCI DSS, and HIPAA
Attack Prevented: Undetected data exfiltration, insider abuse, repudiation, delayed breach detection
ClickOps Implementation
Step 1: Enable Auditing
- Navigate to: Project → Database Deployments
- Select cluster → Auditing
- Enable auditing
- Configure audit filter for events of interest
Step 2: Configure Log Push to External Storage or a SIEM
Atlas push-based log delivery now covers a broader set of destinations than object storage alone:
| Destination | Notes |
|---|---|
| AWS S3 | Supports server-side encryption with your own KMS key (SSE-KMS) as of February 2026 — use it, so the log archive carries the same key control as your data (control 3.2) |
| Google Cloud Storage | Added March 2026 |
| Azure Blob Storage | Added March 2026 |
| Datadog | Added March 2026 |
| Splunk | Added March 2026 |
| OpenTelemetry | Added March 2026 — use where you want a vendor-neutral pipeline into an existing collector |
| Atlas Data Federation | Query logs in place without moving them |
- Navigate to: Project → Integrations
- Select the destination and provide its credentials or role
- For S3, configure SSE-KMS with a customer-managed key rather than accepting default encryption
- Confirm logs arrive at the destination before you rely on the pipeline — a silently failing export is worse than no export, because it looks like coverage
Code Pack: Terraform
# -----------------------------------------------------------------------------
# 4.1 Database Auditing
# Captures authentication, authorization, and DDL events. The audit filter
# controls which operations are logged. Enable audit_authorization_success
# at L2+ for full authorization visibility (higher log volume).
# -----------------------------------------------------------------------------
resource "mongodbatlas_auditing" "config" {
project_id = var.atlas_project_id
audit_filter = var.audit_filter
audit_authorization_success = var.audit_authorization_success
enabled = true
}
Code Pack: API Script
# Retrieve current auditing configuration
AUDIT_CONFIG=$(atlas_get "/groups/${ATLAS_PROJECT_ID}/auditing") || {
fail "4.1 Failed to retrieve auditing configuration (requires M10+ cluster)"
increment_failed
summary
exit 1
}
AUDIT_ENABLED=$(echo "${AUDIT_CONFIG}" | jq -r '.enabled // false')
AUDIT_FILTER=$(echo "${AUDIT_CONFIG}" | jq -r '.auditFilter // "none"')
AUDIT_AUTH_SUCCESS=$(echo "${AUDIT_CONFIG}" | jq -r '.auditAuthorizationSuccess // false')
if [ "${AUDIT_ENABLED}" = "true" ]; then
pass "4.1 Database auditing is enabled"
info "4.1 Current audit filter: ${AUDIT_FILTER}"
info "4.1 Audit authorization success: ${AUDIT_AUTH_SUCCESS}"
increment_applied
else
warn "4.1 Database auditing is DISABLED -- enabling now..."
# Enable auditing with a comprehensive filter
AUDIT_PAYLOAD='{
"enabled": true,
"auditFilter": "{\"$or\":[{\"users\":[]},{\"atype\":{\"$in\":[\"authCheck\",\"authenticate\",\"createCollection\",\"createDatabase\",\"createIndex\",\"dropCollection\",\"dropDatabase\",\"dropIndex\",\"createUser\",\"dropUser\",\"updateUser\",\"grantRolesToUser\",\"revokeRolesFromUser\",\"createRole\",\"dropRole\",\"updateRole\",\"shutdown\"]}}]}",
"auditAuthorizationSuccess": false
}'
RESULT=$(atlas_patch "/groups/${ATLAS_PROJECT_ID}/auditing" "${AUDIT_PAYLOAD}") || {
fail "4.1 Failed to enable auditing"
increment_failed
summary
exit 1
}
NEW_STATUS=$(echo "${RESULT}" | jq -r '.enabled // false')
if [ "${NEW_STATUS}" = "true" ]; then
pass "4.1 Database auditing enabled successfully"
increment_applied
else
fail "4.1 Auditing enable request returned but status is still disabled"
increment_failed
fi
fi
# L2 check: audit authorization success should be enabled
if should_apply 2 2>/dev/null; then
if [ "${AUDIT_AUTH_SUCCESS}" != "true" ]; then
warn "4.1 L2 recommends enabling auditAuthorizationSuccess for full visibility"
else
pass "4.1 L2: auditAuthorizationSuccess is enabled"
fi
fi
Code Pack: Sigma Detection Rule
detection:
selection:
eventTypeName:
- 'AUDIT_CONFIGURATION_UPDATED'
- 'PROJECT_AUDIT_CONFIGURATION_UPDATED'
filter_disabled:
enabled: false
condition: selection and filter_disabled
fields:
- username
- remoteAddress
- groupId
- enabled
- auditFilter
- created
4.2 Monitor Atlas Activity Feed
Profile Level: L1 (Crawl)
| Framework | Control |
|---|---|
| CIS Controls | 8.11 |
| NIST 800-53 | AU-6 |
Description
Monitor Atlas Activity Feed for administrative and security events.
Rationale
Why This Matters:
- The Activity Feed records administrative actions — user logins, configuration changes, and access-list edits — that signal account compromise or misconfiguration
- Alerting on failed authentication attempts surfaces brute-force and credential-stuffing attempts before they succeed
- Alerting on configuration changes catches unauthorized modifications such as opening the IP access list or adding privileged users
- Continuous monitoring shortens detection and response time, limiting an attacker’s dwell time
Attack Prevented: Account takeover, unauthorized configuration change, brute-force attempts, delayed incident response
ClickOps Implementation
Step 1: Access Activity Feed
- Navigate to: Project → Activity Feed
- Review recent events:
- User authentication
- Configuration changes
- Alerts
Step 2: Configure Alerts
- Navigate to: Project → Alerts
- Create alerts for:
- Failed authentication attempts
- Configuration changes
- Resource threshold violations
5. Compliance Quick Reference
SOC 2 Trust Services Criteria Mapping
| Control ID | Atlas Control | Guide Section |
|---|---|---|
| CC6.1 | MFA for console | 2.2 |
| CC6.1 | Database users | 2.1 |
| CC6.6 | Network access | 1.1 |
| CC6.7 | Encryption | 3.1 |
| CC7.2 | Auditing | 4.1 |
NIST 800-53 Rev 5 Mapping
| Control | Atlas Control | Guide Section |
|---|---|---|
| SC-7 | Network security | 1.1, 1.2 |
| AC-6 | Least privilege | 2.1 |
| IA-2(1) | MFA | 2.2 |
| SC-28 | Encryption at rest | 3.1 |
| AU-2 | Auditing | 4.1 |
Appendix A: Tier Compatibility
MongoDB has restructured the Atlas tier model. The current tiers are Free, Flex, and Dedicated:
- Free — the tier previously known as M0, now branded “Free cluster”
- Flex — supersedes the former M2 and M5 shared tiers
- Dedicated — M10 and above
- Serverless — creation of new Serverless instances was removed on 2025-10-22; Flex is the replacement for that workload shape
| Feature | Free | Flex | Dedicated (M10+) |
|---|---|---|---|
| IP Access List | ✅ | ✅ | ✅ |
| Atlas Resource Policies (org-level) | ✅ | ✅ | ✅ |
| VPC Peering | ❌ | ❌ | ✅ |
| Private Endpoints | ❌ | ❌ | ✅ |
| CMK Encryption | ❌ | ❌ | ✅ |
| Database Auditing | ❌ | ❌ | ✅ |
| Database Access Tracking | ❌ | ❌ | ✅ |
| Backup Compliance Policy | ❌ | ❌ | ✅ |
| X.509 Certificate Authentication | ❌ | ❌ | ✅ |
Flex clusters do not support the private-connectivity, key-management, or auditing controls in this guide. If a workload requires controls 1.2, 3.2, 3.4, or 4.1, it requires a Dedicated cluster — the hardening posture is a tier decision, not only a configuration decision.
References: Free cluster limitations · Flex cluster limitations
Appendix B: References
Official MongoDB Documentation:
- MongoDB Atlas Product Documentation
- Atlas Security Features
- Network Security Guidance
- Security Checklist
- Atlas Resource Policies
- Backup Compliance Policy
- Configure Atlas Administration API Access
- Multi-Factor Authentication
- Configure Database Authentication
- Atlas User Roles
API Documentation:
Compliance Frameworks:
- MongoDB states Atlas holds SOC 2 Type II, ISO/IEC 27001:2022, ISO 27017, ISO 27018, ISO 9001, PCI DSS v4.0, and CSA STAR Level 2. Obtain current attestation reports directly from MongoDB under NDA — certifications describe MongoDB’s own posture, not your configuration.
- MongoDB Atlas Compliance Features — the configurable features that support your compliance obligations
Hardening Benchmarks:
-
CIS MongoDB 8 Benchmark v1.0.0
Scope note: the CIS MongoDB benchmark targets the self-managed MongoDB server — operating-system hardening,
mongodconfiguration file settings, filesystem permissions, and process-level auditing. It does not cover the Atlas control plane, and most of its recommendations are either not applicable to a managed deployment or already enforced by Atlas. Use it for self-managed MongoDB; use this guide for Atlas.
Security Incidents:
- Corporate Systems Breach (December 2023): MongoDB detected unauthorized access to corporate systems on December 13, 2023 via a phishing attack. Customer names, phone numbers, email addresses, and account metadata were exposed. One customer’s system logs were accessed. MongoDB Atlas cluster data was NOT affected — the attackers never accessed Atlas clusters or the Atlas authentication system. — MongoDB Security Incident Update
Changelog
| Date | Version | Maturity | Changes | Author |
|---|---|---|---|---|
| 2026-08-08 | 0.2.0 | draft | Currency pass. Repaired the cheat-sheet parser contract by adding missing Attack Prevented lines to 1.1, 1.2, 2.1, 3.2, and 3.3. New controls: 1.3 Atlas Resource Policies, 2.5 Administration API hardening, 3.4 Backup Compliance Policy (section 3 retitled Encryption & Data Protection to host it). Corrected 2.2 for SMS deprecation and the current factor list, 2.1 for the MongoDB 8.0 LDAP deprecation plus OIDC Workforce/Workload Identity Federation, and 2.4 with the full org/project role tables and the Project Access Manager escalation path. Added AWS public-IPv4 rotation and M10/M20 connection-rate callouts, expanded 4.1 log push destinations, rewrote Appendix A for the Free/Flex/Dedicated tier model, removed Trust Center and compliance-report-request references, and re-cited CIS as the MongoDB 8 Benchmark v1.0.0 with an Atlas scope note. Tier 3/4 research not surveyed this pass. | Claude Code (Opus 4.8) |
| 2026-06-29 | 0.1.1 | draft | Add cheat-sheet Description and Rationale for all controls | Claude Code (Opus 4.8) |
| 2025-02-05 | 0.1.0 | draft | Initial guide with network, authentication, and encryption controls | Claude Code (Opus 4.5) |
Contributing
Found an issue or want to improve this guide?
- Report outdated information: Open an issue with tag
content-outdated - Propose new controls: Open an issue with tag
new-control - Submit improvements: See Contributing Guide