Skip to main content
emnode
Compliance

Harden Azure Cosmos DB

One capability across every Cosmos DB API: make sure no database account is reachable from the public internet, by routing access through a private endpoint and shutting public network access off, unless you genuinely intend a store to be public.

13 min·10 sections·AZURE

Last reviewed

Hardening Cosmos DB: the basics

What does a publicly reachable Cosmos DB account actually look like?

An Azure Cosmos DB account is one of the most data-dense objects in a subscription, and its exposure shows up as network reachability rather than a single switch. By default a new account is reachable from any network: its endpoint resolves on public DNS and accepts connections from the whole internet, with access gated only by keys or tokens. There are two distinct ways to close that door, and Defender for Cloud raises each as its own recommendation. The first is whether the account is mapped to an Azure Private Link private endpoint, so a virtual network can reach it over a private IP. The second is whether the account's 'publicNetworkAccess' property is set to Disabled, so the public route is shut even before, or independently of, any private endpoint.

These two read as separate findings, 'Cosmos DB accounts should use private link' and 'Azure Cosmos DB should disable public network access', but they are one job. A private endpoint gives you a private path in; disabling public network access removes the public path entirely. You want both, because a private endpoint alone does not close the public route. As Microsoft's own documentation notes, once approved private endpoints are removed or rejected, an account is open to the entire network again unless 'publicNetworkAccess' is set to Disabled. The two recommendations are the front and back of the same lock.

Most of this exposure is drift, not intent. An account created before private endpoints were wired into the landing zone, a module that never set 'publicNetworkAccess', a quick proof of concept that was left reachable because restricting it needed a virtual network nobody had time to build. The work is to find every account reachable from the public internet, decide which handful are genuinely meant to be, give the rest a private endpoint, and then disable public network access so the public route cannot reopen by accident.

In this lesson you will learn how Azure Cosmos DB expresses public reachability, why a private endpoint and disabling public network access are two halves of the same lock, how to find every publicly reachable account in a subscription, and how to lock them down without cutting off the applications that depend on them. The Controls this lesson covers section lists every Defender for Cloud recommendation in this capability, each linking to a deep page with the exact check and a copy-and-paste fix.

Fun fact

The database that answered the whole internet

A recurring pattern in cloud data exposures is a database that was reachable from the public internet with access resting on a single key or token, found by an automated scanner long before the owner noticed. Cosmos DB endpoints resolve on public DNS by default, which is exactly why Microsoft built two separate guards: a private endpoint to give the database a private path in, and a 'publicNetworkAccess' flag to shut the public path entirely. The detail that catches teams out is that the two are independent. Remove the last private endpoint and an account reopens to the whole network unless public network access was explicitly disabled. Belt and braces is the design, not an overreaction.

Finding publicly reachable Cosmos DB accounts

Sam is the security lead at a scale-up preparing for its first ISO 27001 audit. Defender for Cloud shows Cosmos DB findings across several accounts in two subscriptions that pre-date the team's private-endpoint guardrails.

Rather than work the findings one by one, Sam starts by listing which accounts still have public network access enabled, so the genuinely public stores can be separated from drift before anything changes.

List every Cosmos DB account with its public-network-access state. 'Enabled' means the account answers the public internet.

$ az cosmosdb list --query "[].{name:name, rg:resourceGroup, publicAccess:publicNetworkAccess}" -o table
Name Rg PublicAccess
---------------- -------------- ------------
custprofiles-prod data-prod Enabled
sessions-prod data-prod Disabled
scratch-dev sandbox Enabled
# custprofiles-prod holds customer data AND answers the public internet. Close it first.

An account that holds real data and still has public network access enabled is the highest-value target in this group. Fix these first, after giving each a private path in.

How Defender for Cloud decides a Cosmos DB account is exposeddeep dive

Both recommendations in this capability resolve to network reachability, evaluated on the account's properties. 'Cosmos DB accounts should use private link' checks whether the account has an approved private endpoint connection mapping it into a virtual network over a private IP. 'Azure Cosmos DB should disable public network access' checks the 'publicNetworkAccess' property: when it is Enabled, the account's endpoint accepts connections from the public internet subject only to any IP or virtual-network firewall rules, and when it is Disabled, the public route is shut entirely.

The interaction between the two matters and is easy to get wrong. Adding a private endpoint does not, on its own, disable the public route. Microsoft's documentation is explicit that if no public-traffic or service-endpoint firewall rules are configured, an account is reachable only through its private endpoints, but the moment all approved private endpoints are rejected or deleted the account is open to the entire network again, unless 'publicNetworkAccess' is set to Disabled. Setting that flag takes precedence over any IP or virtual-network rule: all public and virtual-network traffic is blocked when it is Disabled, even if a source address is allowed in the firewall. That is why the two recommendations pair: the private endpoint provides the way in, and disabling public network access removes the way the public can still get in.

Defender for Cloud evaluates these against the Microsoft Cloud Security Benchmark on a periodic cycle, so a fix does not flip the recommendation to Healthy instantly. The control-plane change is immediate, but the posture catches up on the next assessment. The strongest position combines the account settings with an Azure Policy backstop that audits for private link and for disabled public network access, so a misconfigured account is surfaced rather than silently left open.

What is the impact of leaving Cosmos DB on the public internet?

The direct impact is data exposure. A Cosmos DB account reachable from the public internet is gated by keys and tokens alone, and a leaked primary key, a connection string committed to a repository, or an over-broad resource token is enough to read or write the entire account from anywhere. Endpoints resolve on public DNS by default, so a reachable account is discoverable, and internet-facing data stores are found quickly by automated scanners. Taking the account off the public internet means a leaked credential is no longer sufficient on its own: an attacker also needs a foothold inside your network.

The second-order impact is blast radius and data exfiltration. An account on the public internet is attack surface that must be defended continuously, and it is also an exfiltration path: data can be pushed out to the open internet from a compromised application. Routing access through a private endpoint, combined with restrictive network security group rules, is exactly the configuration Microsoft recommends to reduce the risk of data exfiltration, because traffic to the account stays on private IPs inside the network.

On the compliance side, every modern framework, ISO 27001, SOC 2, HIPAA, PCI DSS and the UK and EU data-protection regimes, expects evidence that data stores are not reachable from untrusted networks. Both recommendations in this capability map to the Microsoft Cloud Security Benchmark control NS-2, secure cloud services with network controls, which is the control auditors look to for private access points. A Cosmos DB estate that uses private link and has public network access disabled is among the cheapest and most defensible artefacts you can hand an auditor.

How do you take Cosmos DB off the public internet safely?

Work the capability as one loop rather than chasing the two findings separately. The order matters: give an account a private path in before you shut the public one, so you do not cut off the applications that depend on it.

1. Inventory every account by reachability

List every Cosmos DB account with its 'publicNetworkAccess' state and whether it has an approved private endpoint. Treat this inventory as the source of truth, not the Defender finding count, because the two recommendations describe the front and back of the same lock and an account can trip both at once.

2. Separate genuine public stores from drift

Decide for each account whether anything legitimately needs to reach it from outside your network. The great majority do not: production data accounts should be reachable only from inside the network. The rare account that must accept outside traffic should still be reached through a controlled front door rather than left directly open.

3. Add the private path, then shut the public one

Create a private endpoint for each account that an application reaches over the network, mapping the correct API sub-resource (Sql, MongoDB, Cassandra, Gremlin or Table) and integrating it with a private DNS zone. Wait for the connection to be approved and DNS to resolve privately, then set 'publicNetworkAccess' to Disabled. Doing it in this order means the application keeps a working path throughout. To avoid the short reconfiguration downtime on an existing account, allow your client through a temporary firewall rule, wait ten minutes, add the private endpoint, then remove the rule.

4. Backstop it with Azure Policy

Assign the built-in policies that audit for private link and for disabled public network access, so any new account left reachable from the public internet is surfaced on the next assessment rather than discovered in an incident. Both policies map to the Microsoft Cloud Security Benchmark control NS-2.

# 1. Give the account a private path IN before shutting the public one.
#    --group-ids is the API sub-resource: Sql, MongoDB, Cassandra, Gremlin or Table.
RG=data-prod
ACCT=custprofiles-prod
VNET=spoke-data-vnet
SUBNET=db-subnet
SUB=$(az account show --query id -o tsv)

az network private-endpoint create \
  --name "${ACCT}-pe" \
  --resource-group "$RG" \
  --vnet-name "$VNET" \
  --subnet "$SUBNET" \
  --private-connection-resource-id \
    "/subscriptions/$SUB/resourceGroups/$RG/providers/Microsoft.DocumentDB/databaseAccounts/$ACCT" \
  --group-ids Sql \
  --connection-name "${ACCT}-conn"

# 2. Once the endpoint is approved and private DNS resolves, shut the public route.
#    --public-network-access Disabled takes precedence over any IP/VNet firewall rule.
az cosmosdb update \
  --name "$ACCT" \
  --resource-group "$RG" \
  --public-network-access Disabled

# 3. Backstop: audit any future account that lacks private link or stays public.
#    Look the built-in policies up by display name; never hardcode a definition GUID.
for disp in \
  "CosmosDB accounts should use private link" \
  "Azure Cosmos DB should disable public network access"; do
  pid=$(az policy definition list \
    --query "[?displayName=='$disp'].name | [0]" -o tsv)
  az policy assignment create \
    --name "audit-$(echo "$disp" | tr ' A-Z' '-a-z' | cut -c1-40)" \
    --policy "$pid" \
    --scope "/subscriptions/$SUB"
done

Quick quiz

Question 1 of 5

Defender for Cloud shows both 'Cosmos DB accounts should use private link' and 'Azure Cosmos DB should disable public network access' on the same account. How should you think about them?

You can now treat Azure Cosmos DB as one capability rather than two separate findings: inventory which accounts answer the public internet, give each production account a private endpoint as a private path in, disable public network access so the public route cannot reopen by accident, and back it with an Azure Policy audit. The Controls this lesson covers section below links every recommendation in this group to its deep page and fix.

Back to the library

Controls this lesson covers

One capability, many Microsoft Defender for Cloud recommendations. This lesson is the shared playbook; each control below keeps its own deep page with the exact check, severity and a copy-and-paste fix.