Back to blog
AzureBest PracticesCloud SecurityOperations & ComplianceStorage

Azure Blob Container Has Public Access: How to Find and Fix It

Public Azure Blob containers expose data to anyone on the internet. Learn how to detect, fix, and prevent anonymous blob access with CLI, Terraform, and policy.

TL;DR

This check flags Azure Blob containers configured for anonymous public access, which lets anyone on the internet read your objects without credentials. Disable public access at the storage account level with --allow-blob-public-access false and serve files through SAS tokens or a CDN instead.

Public blob containers are one of the most common causes of accidental data exposure in Azure. A single misconfigured container can put internal documents, backups, customer records, or application secrets a Google search away from anyone. This check catches containers that permit anonymous reads before someone else finds them first.


What this check detects

The storage_blobpublic check inspects your Azure Storage accounts and the Blob containers inside them for anonymous public access. In Azure, public access works on two levels:

  • Account level: the allowBlobPublicAccess property. When this is true, individual containers are allowed to be made public.
  • Container level: the publicAccess setting, which can be None, Blob, or Container.

The two public values mean different things:

  • Blob grants anonymous read access to the blobs themselves, but not to the container listing.
  • Container grants anonymous read access to the blobs and lets anyone enumerate every object in the container.

Note: A container can only be set to public if the parent storage account has allowBlobPublicAccess enabled. Turning that account-level flag off is the single most effective way to close this hole across every container at once.

The check fails when a container is set to Blob or Container access, or when the account has public access enabled and offers no guardrail against it.


Why it matters

Anonymous access removes authentication entirely. There is no IAM, no key, no SAS token, just an unauthenticated HTTPS GET. If an attacker can guess or discover the URL, they can read the data.

The real-world failure pattern usually looks like this:

  1. A developer sets a container to Container access to quickly share a few public assets.
  2. Over time, the container becomes a dumping ground for other files: database exports, log archives, config files.
  3. Because the access mode allows listing, a scanner walks the entire container and pulls everything.

Attackers actively scan for this. Storage account URLs follow a predictable pattern (https://<account>.blob.core.windows.net/<container>), and tools exist purely to brute-force account and container names. A public container with listing enabled needs no guessing of individual blob names at all.

Danger: Public Container access is the worst case. It lets an anonymous user enumerate every blob, so one forgotten sensitive file in an otherwise public bucket exposes everything. Treat any container set to Container as an active incident until you confirm its contents are genuinely public data.

The business impact is concrete: exposed PII triggers breach notification obligations under GDPR, HIPAA, and similar regimes; exposed credentials lead to account takeover; and exposed backups hand attackers a full copy of your data with no logging that ties back to a compromised identity.


How to fix it

Start by finding out which accounts and containers are affected, then remediate.

1. Audit your storage accounts

List every storage account and its public access setting:

az storage account list \
  --query "[].{name:name, group:resourceGroup, publicAccess:allowBlobPublicAccess}" \
  --output table

For a specific account, list containers and their access level:

az storage container list \
  --account-name mystorageaccount \
  --auth-mode login \
  --query "[].{name:name, publicAccess:properties.publicAccess}" \
  --output table

2. Disable public access at the account level

This is the recommended fix. It blocks public access for all containers in the account in one operation:

az storage account update \
  --name mystorageaccount \
  --resource-group my-resource-group \
  --allow-blob-public-access false

Warning: If any application legitimately serves public assets straight from this account (a static website, public images, downloads), flipping this flag will break those requests immediately. Confirm what each public container is used for before disabling, and plan to move genuinely public content behind a SAS token or CDN first.

3. Or set a single container back to private

If you need account-level public access to stay on for legitimate reasons, lock down the offending container individually:

az storage container set-permission \
  --name leaky-container \
  --account-name mystorageaccount \
  --auth-mode login \
  --public-access off

4. Fix it in the Azure Portal

  1. Open the storage account, then go to Settings > Configuration.
  2. Set Allow Blob anonymous access to Disabled and save.
  3. To handle a single container instead, go to Data storage > Containers, select the container, choose Change access level, and set it to Private (no anonymous access).

5. Serve public content the right way

If files genuinely need to be public, do not reach for anonymous access. Use a time-limited SAS token so access is scoped and revocable:

az storage blob generate-sas \
  --account-name mystorageaccount \
  --container-name assets \
  --name logo.png \
  --permissions r \
  --expiry 2025-12-31T23:59:59Z \
  --auth-mode login \
  --as-user \
  --full-uri

Tip: For high-traffic public assets, put Azure CDN or Front Door in front of a private container using a managed identity origin. You get caching, custom domains, and TLS without ever exposing the storage account directly.


How to prevent it from happening again

Manual fixes do not last. Anyone with Contributor rights can re-enable public access tomorrow. Enforce the rule at the platform level.

Azure Policy

Azure ships a built-in policy that denies storage accounts with public blob access. Assign it at the subscription or management group scope:

az policy assignment create \
  --name deny-public-blob \
  --display-name "Deny storage accounts with public blob access" \
  --scope "/subscriptions/<subscription-id>" \
  --policy "4733ea7b-a883-42fe-8cac-97454c2a9e4a" \
  --params '{"effect": {"value": "Deny"}}'

With this in place, any attempt to create or update an account with public access set to enabled is rejected at deployment time.

Set the default in IaC

Bake the safe setting into your templates so new accounts are private by default. In Terraform:

resource "azurerm_storage_account" "main" {
  name                            = "mystorageaccount"
  resource_group_name             = azurerm_resource_group.main.name
  location                        = azurerm_resource_group.main.location
  account_tier                    = "Standard"
  account_replication_type        = "LRS"
  allow_nested_items_to_be_public = false
}

The allow_nested_items_to_be_public = false argument maps directly to the account-level flag and prevents any container in this account from going public.

Gate it in CI/CD

Catch the misconfiguration before it merges. Add a policy-as-code scan to your pipeline using a tool like Checkov against your Terraform plan:

checkov -d ./infra --check CKV_AZURE_59

Fail the build on any violation so a public account never reaches production in the first place.

Tip: Layer continuous detection on top of preventive controls. Lensix runs the storage_blobpublic check across all your subscriptions on a schedule, so a container that slips through (for example via a portal change outside your pipeline) gets flagged within minutes rather than at the next audit.


Best practices

  • Default to private. Disable allowBlobPublicAccess on every new account and only make exceptions with a documented reason.
  • Separate public from private data. Never mix sensitive files into a container that serves public content. Use a dedicated account for genuinely public assets so a mistake there cannot leak internal data.
  • Prefer SAS tokens over anonymous access. Scoped, time-limited, revocable access beats wide-open reads every time.
  • Use a stored access policy for SAS tokens so you can revoke them centrally without rotating account keys.
  • Enable diagnostic logging. Turn on storage analytics and route logs to Log Analytics so you can see who is reading what. Anonymous reads will not have an identity, which makes monitoring even more important.
  • Restrict network access. Combine private access with firewall rules or private endpoints so the account is only reachable from approved networks.
  • Review access levels in code review. Any IaC change that sets a container to Blob or Container should require explicit sign-off.

Public blob access is rarely needed and almost never worth the risk. Disable it by default, serve the genuine exceptions through SAS or a CDN, and enforce the rule with policy so the setting cannot drift back open.