Cloud Security Wire
AWS Azure GCP RSS
Hardening Guide

Azure Blob Storage Public Access Misconfigurations: Exposure, Enumeration, and Hardening

How anonymous public access to Azure Blob Storage gets enabled, how attackers enumerate exposed containers, and a complete hardening guide including Azure Policy enforcement and remediation CLI commands.

By Cloud Security Wire · ·
#azure#blob-storage#misconfiguration#data-exposure#storage-account#azure-policy#hardening#public-access

Azure Blob Storage public access misconfigurations remain one of the most common causes of cloud data exposure. Despite Microsoft adding tenant-level controls in 2023 and making private-by-default the recommended configuration, incident response data consistently shows exposed storage accounts in Azure environments — often containing sensitive documents, application logs, database backups, and build artifacts.

This guide covers how public access works at the API level, how attackers enumerate exposed containers, and how to enforce a hardened configuration across your Azure estate.

How Azure Blob Public Access Works

Azure Blob Storage has two distinct access layers that are frequently confused:

Storage Account Level (allowBlobPublicAccess)
This property, set on the storage account resource itself, acts as a master switch. When set to false, it overrides all container-level settings and prevents anonymous access regardless of container configuration. When set to true (the legacy default), container-level settings take effect.

Container Level (publicAccess property)
Individual containers can be set to:

  • None — private, requires authentication
  • Blob — anonymous read access to individual blobs (requires knowing the URL)
  • Container — anonymous read + list access (allows enumeration of all blob names)

The dangerous combination is allowBlobPublicAccess: true on the storage account AND publicAccess: Container on one or more containers. This makes blob contents publicly readable and their names enumerable — without any authentication.

How Attackers Enumerate Exposed Storage

Exposed Azure Blob containers are routinely discovered through automated scanning. The common enumeration path:

Step 1: Discover the storage account endpoint

Storage account URLs follow predictable patterns:

https://<account-name>.blob.core.windows.net/

Attackers use wordlists of common account naming patterns (companyname-backup, companyname-dev, companyname-logs) combined with tools like BlobHunter or simple curl loops.

Step 2: Enumerate containers

If publicAccess: Container is set, listing containers requires no authentication:

# Anonymous container enumeration via REST API
curl "https://mystorageaccount.blob.core.windows.net/?comp=list" 

# Expected response for a misconfigured account
# Returns XML listing all public container names

Step 3: List and download blobs

# List blobs in an exposed container
curl "https://mystorageaccount.blob.core.windows.net/backups?restype=container&comp=list"

# Download a specific blob
curl -O "https://mystorageaccount.blob.core.windows.net/backups/database-backup-2026-05-30.sql.gz"

Using the Azure CLI, a misconfigured container can be dumped in seconds:

az storage blob list \
  --account-name mystorageaccount \
  --container-name backups \
  --output table \
  --no-auth-required

Auditing for Exposed Storage Accounts

Find accounts with public access enabled (Azure CLI)

# List all storage accounts in a subscription with public blob access allowed
az storage account list \
  --query "[?allowBlobPublicAccess==true].{Name:name, RG:resourceGroup, Location:location}" \
  --output table

# Check a specific storage account
az storage account show \
  --name mystorageaccount \
  --resource-group myresourcegroup \
  --query "{AllowPublicAccess:allowBlobPublicAccess, DefaultAction:networkRuleSet.defaultAction}" \
  --output json

Find containers with public access set

# For each storage account with public access enabled, check containers
az storage container list \
  --account-name mystorageaccount \
  --auth-mode login \
  --query "[?properties.publicAccess != 'None'].{Name:name, Access:properties.publicAccess}" \
  --output table

Azure Resource Graph query for tenant-wide visibility

Resources
| where type == "microsoft.storage/storageaccounts"
| where properties.allowBlobPublicAccess == true
| project name, resourceGroup, location, subscriptionId,
          kind = properties.kind,
          sku = sku.name
| order by subscriptionId, resourceGroup

Run this in Azure Resource Graph Explorer or via CLI:

az graph query -q "
Resources
| where type == 'microsoft.storage/storageaccounts'
| where properties.allowBlobPublicAccess == true
| project name, resourceGroup, location
"

Remediation

Disable public access on a storage account

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

Set all containers in an account to private

# Get all containers and set to private
for container in $(az storage container list \
  --account-name mystorageaccount \
  --auth-mode login \
  --query "[].name" \
  --output tsv); do
    az storage container set-permission \
      --name "$container" \
      --account-name mystorageaccount \
      --public-access off \
      --auth-mode login
    echo "Set $container to private"
done

Enforcing Policy at Scale with Azure Policy

Remediation alone is not enough — without preventive controls, new storage accounts can be created with public access enabled. Azure Policy provides guardrails.

Built-in policy: Storage account public access should be disallowed

Microsoft provides a built-in policy definition for this:

# Assign the built-in "Storage account public access should be disallowed" policy
az policy assignment create \
  --name "deny-blob-public-access" \
  --display-name "Deny public blob access on storage accounts" \
  --policy "4fa4b6c0-31ca-4c0d-b10d-24b96f62a751" \
  --scope "/subscriptions/<subscription-id>" \
  --enforcement-mode Default

Custom policy for audit and deny

{
  "mode": "All",
  "policyRule": {
    "if": {
      "allOf": [
        {
          "field": "type",
          "equals": "Microsoft.Storage/storageAccounts"
        },
        {
          "field": "Microsoft.Storage/storageAccounts/allowBlobPublicAccess",
          "equals": "true"
        }
      ]
    },
    "then": {
      "effect": "Deny"
    }
  }
}

Apply this as a deny policy at the management group level to cover all child subscriptions.

Additional Hardening Steps

Enable Microsoft Defender for Storage

Defender for Storage detects anomalous access patterns — including anonymous access to previously private containers and unusual geographic access. Enable per storage account or at the subscription level:

az security pricing create \
  --name StorageAccounts \
  --tier Standard

Require Azure AD authentication only

Disable shared key access to force all requests through Azure AD:

az storage account update \
  --name mystorageaccount \
  --resource-group myresourcegroup \
  --allow-shared-key-access false

Network restrictions

Limit storage account access to specific virtual networks or IP ranges:

az storage account update \
  --name mystorageaccount \
  --resource-group myresourcegroup \
  --default-action Deny \
  --bypass AzureServices

Monitor with Azure Monitor and Defender for Cloud

Enable diagnostic logging to capture all read/write/delete operations. The StorageBlobLogs table in Log Analytics is searchable for anonymous access events:

StorageBlobLogs
| where AuthenticationType == "Anonymous"
| where TimeGenerated > ago(7d)
| summarize Count = count(), Bytes = sum(ResponseBodySize)
    by bin(TimeGenerated, 1h), CallerIpAddress, Uri
| order by Count desc

Public access misconfigurations in Azure storage are preventable with a combination of tenant-level defaults, Azure Policy enforcement, and monitoring. The critical step is applying the deny policy at management group scope — remediation of individual accounts without preventive controls is a continuous manual burden.

← All Analysis Subscribe via RSS