Here's the trap that catches most teams: any storage account created before 2023 shipped with the anonymous-access gate wide open, and almost nobody went back to close it. When Microsoft flipped the default, existing accounts were left exactly as they were. So the account that's been quietly serving traffic since 2021 is very likely still set to allow anonymous reads. If a container under it has its public access level set to blob or container, anyone holding the storage account hostname can pull files down with no Azure account, no key, nothing.

How Azure Blob containers become publicly readable

Azure Blob anonymous access is controlled by two layered settings that must both be permissive for exposure to occur.

The account-level toggle. Every storage account has a setting called "Allow Blob anonymous access" (surfaced in the Azure portal under Settings → Configuration). When this is set to Disabled, anonymous access is blocked at the authorization layer regardless of what any individual container specifies (the request still reaches the service, it just gets rejected). Microsoft made Disabled the default for newly created storage accounts starting in 2023, but storage accounts created before that change were not retroactively updated. They still default to Enabled, meaning the account-level gate is open.

The container-level public access level. Even when the account allows anonymous access, each container has its own public access level:

  • Private: no anonymous access; authentication required for all requests.
  • Blob: individual blobs can be read anonymously if the caller knows the exact URL, but the container's contents cannot be enumerated.
  • Container: anonymous callers can enumerate the container (list all blobs) and download any blob without authentication.

The Container access level is the more dangerous of the two public options. It combines directory listing with full object read, so an attacker who only knows the storage account hostname and container name has full access to every file inside.

The common path to exposure: a developer sets a container to Container access during a prototype or data-migration phase so a colleague can access files without going through Azure AD. The project ships, the container access level is never changed back, and the storage account's account-level toggle was never set to Disabled because the account predates the 2023 default change.

How to detect anonymous access

Unauthenticated container enumeration. If a container's public access level is set to Container, the following request returns an XML manifest of all blobs inside it with no credentials or Authorization header required:

GET https://<account>.blob.core.windows.net/<container>?restype=container&comp=list

A 200 response with <EnumerationResults> XML confirms the container is publicly listable. The error responses are easy to mix up:

  • 409 PublicAccessNotPermitted: the account-level toggle is Disabled. The container's own setting is irrelevant at this point; the account gate shut you out.
  • 404 ContainerNotFound (a variant of ResourceNotFound): the container is either private or simply doesn't exist. Azure deliberately won't tell you which, so a 404 here is not a signal that you've found a real-but-locked container.
  • 403 AuthorizationFailure: a distinct case, returned when the request carries credentials or conditions that don't authorize the operation. Don't conflate it with the 404 above; they mean different things.

Azure CLI account-level check. If you own or have access to the storage account:

az storage account show \
  --name <account-name> \
  --resource-group <rg-name> \
  --query "allowBlobPublicAccess"

A true result means the account-level gate is open. Check individual container access levels:

az storage container list \
  --account-name <account-name> \
  --query "[*].{name:name, publicAccess:properties.publicAccess}"

Any container showing container or blob under publicAccess should be reviewed.

Check a container here. Paste the blob URL or hostname into the open-bucket viewer and the tool will run the ?restype=container&comp=list probe and report whether an anonymous caller can enumerate the contents.

What's at risk

Container-level access (listable + readable) is effectively equivalent to an open S3 bucket. A caller who issues the ?restype=container&comp=list request gets an XML document containing every blob name, its size, content type, and last-modified timestamp. They can then download each blob individually with a direct GET request. Common findings include:

  • Database backup files (*.bak, *.sql, *.dump) containing customer records, credentials, and schema details.
  • Application configuration exports with connection strings, API keys, and secrets that were stored outside a key vault.
  • Log files recording user activity, internal IP addresses, and session identifiers.
  • Document libraries and file uploads from line-of-business applications.

Blob-level access (readable but not enumerable) still bites. The attacker can't list the container, but predictable naming often makes that unnecessary. Names like backup-2024-01-15.zip or export-users.csv are directly fetchable with a single GET, and knowing one date-stamped backup name reveals the format for every other day. Certificate transparency logs can widen the exposure: they leak custom CDN subdomains tied to an account (the hostnames, not the blob paths; paths never show up in CT), handing an attacker the front door to start guessing against.

Remediation

Disable anonymous access at the account level. Start here. One setting shuts the gate for every container under the account, so you don't have to chase each container's own access level. In the Azure portal, navigate to the storage account → Settings → Configuration and set "Allow Blob anonymous access" to Disabled. Via the Azure CLI:

az storage account update \
  --name <account-name> \
  --resource-group <rg-name> \
  --allow-blob-public-access false

Disabling this at the account level overrides any container-level blob or container setting, immediately blocking anonymous access to all containers in that account.

Set container access to Private. For any container that should not be public, change its public access level to Private:

az storage container set-permission \
  --account-name <account-name> \
  --name <container-name> \
  --public-access off

Replace permanent anonymous access with SAS tokens. When you need to share individual blobs or containers with external users for a limited time, use Shared Access Signatures (SAS) instead of anonymous public access. A SAS token grants narrowly scoped permission (read-only, specific blob, specific time window) and does not expose surrounding container contents:

az storage blob generate-sas \
  --account-name <account-name> \
  --container-name <container-name> \
  --name <blob-name> \
  --permissions r \
  --expiry 2026-06-16T00:00:00Z

This produces a time-limited token that grants read access to one blob only. When the token expires, access is revoked automatically.

One caveat on SAS type: prefer a user-delegation SAS (signed with an Azure AD key) or a stored-access-policy SAS over an account-key SAS. The account-key variety can't be revoked before expiry without rotating the storage account key, which kills every other token signed with that key too. A user-delegation SAS dies when you revoke the delegation key, and a stored-access-policy SAS dies when you delete the policy, so both give you a real off switch.

Audit with Azure Policy. Azure provides built-in policy definitions that can be assigned at the subscription or management-group level to flag or deny storage accounts where anonymous access is enabled. Assigning a deny-effect policy prevents new storage accounts from being created with the toggle enabled, and a remediation task can apply the fix to existing accounts at scale.

Rotate credentials found in previously-public containers. If a container was publicly accessible for any period, treat every secret or key it contained as compromised and rotate it: connection strings, API keys, and anything else that lived in blob contents or blob names. Public exposure has no audit trail you can trust to scope the blast radius, so assume the worst and rotate.


For a full index of publicly accessible Azure Blob containers found by this service, see the Azure Blob exposure index. To probe a specific container URL right now, use the open-bucket viewer.