This check flags an Azure Storage container holding log data that allows anonymous public access. Logs often leak internal IPs, request paths, tokens, and user identifiers, so disabling public access at both the account and container level is the fix.
Logs are some of the most sensitive data you store, even though we rarely treat them that way. They record who connected, from where, with which user agent, against which endpoints, and sometimes with which credentials in the query string. When the container that holds those logs is readable by anyone on the internet, you have effectively published your infrastructure's internal narrative to the world.
The Log Container Has Public Access check looks for exactly that condition in Azure Blob Storage: a container used for logs whose public access level is set to blob or container rather than private.
What this check detects
Azure Storage containers have a setting called public access level (also called anonymous access level). It has three possible values:
- Private (no anonymous access) — only authenticated requests with valid keys, SAS tokens, or Azure AD credentials can read the data.
- Blob — anyone can read individual blobs anonymously if they know the URL, but cannot list the container.
- Container — anyone can both read blobs and list everything in the container anonymously.
This check fires when a container that stores logs (for example one named $logs, insights-logs-*, logs, or similar) has its public access level set to blob or container. Both values allow anonymous reads, and that is the problem.
Note: For anonymous access to work at all, two things must be true: the storage account must permit it (allowBlobPublicAccess = true), and the individual container must opt in via its public access level. Disabling the account-level toggle blocks public access for every container at once, which makes it the most reliable lever.
Why it matters
Storage logs and diagnostic logs are not benign. Depending on what you have enabled, a public log container can expose:
- Internal and external IP addresses, useful for mapping your network and finding direct targets.
- Full request URIs, which frequently contain SAS tokens, API keys, or session identifiers passed as query parameters.
- Storage account names, container names, and blob paths that reveal your data layout.
- User agents, authentication types, and operation patterns that help an attacker profile your environment.
- Account and subscription identifiers that aid in broader reconnaissance.
The realistic attack path is straightforward. An attacker scanning for open blob endpoints finds your log container set to container access, lists every log file, and downloads them. Inside those logs they find a SAS token that was logged as part of a request URI. That token still has hours left before expiry, and it grants read access to a production data container. No exploit, no credential stuffing, just reading what you left open.
Public log exposure rarely shows up as the headline of a breach. It shows up as the first paragraph of the post-incident timeline, the foothold that made everything after it possible.
There is also a compliance dimension. Frameworks like SOC 2, ISO 27001, PCI DSS, and HIPAA all expect access logs to be protected from unauthorized access. A publicly readable log container is a direct finding against those controls.
How to fix it
You have two levels to work with: the container's public access level and the account-wide toggle. Fix the container first, then harden the account so it cannot recur.
Step 1: Find the offending container
List containers and their public access settings for an account:
az storage container list \
--account-name mystorageacct \
--auth-mode login \
--query "[].{name:name, publicAccess:properties.publicAccess}" \
--output table
Any container showing blob or container in the publicAccess column needs attention.
Step 2: Set the container to private
Warning: If any legitimate service or pipeline reads these logs anonymously, setting the container to private will break it. Confirm that downstream consumers use keys, SAS tokens, or managed identities before you flip the switch.
az storage container set-permission \
--name logs \
--account-name mystorageacct \
--auth-mode login \
--public-access off
The value off corresponds to private (no anonymous access).
Step 3: Disable public access at the account level
This is the durable fix. Turning off allowBlobPublicAccess blocks anonymous reads across the entire account regardless of any container's setting.
Danger: This setting applies to every container in the account, not just your log container. If you have public containers that are intentionally public (static site assets, public downloads), this command will break them. Audit the full account first.
az storage account update \
--name mystorageacct \
--resource-group my-rg \
--allow-blob-public-access false
Step 4: Verify
az storage account show \
--name mystorageacct \
--resource-group my-rg \
--query "allowBlobPublicAccess"
A return value of false confirms the account no longer permits anonymous access.
Tip: Storage analytics logs written to the special $logs container are private by default and cannot be made public through the portal. If you are seeing a public log container, it is almost always a custom one created by an app or pipeline, so check your deployment code rather than assuming it is a platform default.
How to prevent it from happening again
Manual fixes drift. The goal is to make a public log container impossible to deploy, not just to clean up the one you found.
Set the default in your IaC
In Terraform, set both the account flag and the container access type explicitly so reviewers can see the intent:
resource "azurerm_storage_account" "logs" {
name = "mystorageacct"
resource_group_name = azurerm_resource_group.this.name
location = azurerm_resource_group.this.location
account_tier = "Standard"
account_replication_type = "LRS"
allow_nested_items_to_be_public = false
}
resource "azurerm_storage_container" "logs" {
name = "logs"
storage_account_name = azurerm_storage_account.logs.name
container_access_type = "private"
}
In Bicep:
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: 'mystorageacct'
location: location
sku: { name: 'Standard_LRS' }
kind: 'StorageV2'
properties: {
allowBlobPublicAccess: false
}
}
Enforce it with Azure Policy
Azure ships a built-in policy that denies storage accounts allowing blob public access. Assign it at the subscription or management group scope so no new account can opt back in:
az policy assignment create \
--name "deny-public-blob-access" \
--display-name "Storage accounts should prevent public blob access" \
--policy "4fa4b6c0-31ca-4c0d-b10d-24b96f62a751" \
--scope "/subscriptions/<subscription-id>" \
--enforcement-mode Default
Gate it in CI/CD
Run a policy-as-code scan against your Terraform plan before it merges. With Checkov, the relevant checks are CKV_AZURE_59 (account level) and CKV2_AZURE_21 (container level):
checkov -d ./infra --framework terraform \
--check CKV_AZURE_59,CKV2_AZURE_21
Tip: Layer your defenses. Azure Policy catches drift at deploy time, Checkov catches it at PR time, and a continuous scan with Lensix catches whatever slips past both. Each layer covers a different gap, and the overlap is the point.
Best practices
- Disable public access at the account level by default. Treat any account that allows it as a deliberate exception that needs documentation and a clear owner.
- Keep log storage separate. Put logs in a dedicated account or container with its own access policy so a permissive setting on app data never bleeds into your logs.
- Use private endpoints for log access. Where possible, restrict reads to your VNet rather than relying on access keys traveling over the public internet.
- Authenticate log readers with managed identities. Avoid anonymous access and long-lived keys entirely by granting the
Storage Blob Data Readerrole to the identities that consume logs. - Scrub secrets before they reach logs. The real fix for SAS tokens in URIs is to stop logging them. Strip query parameters from request logging where the platform allows it.
- Enable a soft delete and immutability policy on log containers. Attackers who do gain access often try to delete logs to cover their tracks, so make that harder.
Public log containers are a quiet risk. They do not throw errors, they do not slow anything down, and they often sit unnoticed for months until someone scanning the internet finds them first. Lock the account, default your containers to private, and enforce both in code so the question never comes up again.

