azure-storage-public-blob-container

Storage account with public blob access enabled and a public container exposes blob data to unauthenticated internet access.

derived azure emits ExposedToInternet

match

A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.

{'account': None} Contains {'container': None}

where

node_class(?account) == Storage and node_type(?account) == ObjectStorage node_type(?account) matched provider_type == 'Microsoft.Storage/storageAccounts' ?account.properties.allowBlobPublicAccess == true node_type(?container) == ObjectStorage node_type(?container) matched provider_type == 'Microsoft.Storage/storageAccounts/blobServices/containers' ?container.properties.publicAccess in ['Blob', 'Container']

emit

source typeObjectStorage
target typeAnonymousIdentity
source?container
target<AnonymousIdentity>
state logicif ?account.properties.publicNetworkAccess == Disabled: DO NOT EMIT (storage account explicitly blocks all public network access regardless of blob public access settings). Elif ?account.properties.allowBlobPublicAccess == false: DO NOT EMIT (container publicAccess is ignored by the storage service when account flag is false). Elif networkAcls.defaultAction == Deny AND public internet IP not in IP allowlist: CONDITIONAL(network_reachability) — the container property allows anonymous access but the network firewall blocks public internet traffic to the storage endpoint. Elif publicNetworkAccess == Enabled AND networkAcls.defaultAction == Allow: ACTIVE (no network_reachability condition — the endpoint is publicly accessible by design). For publicAccess=Container: ACTIVE (anonymous list-and-read); for publicAccess=Blob: ACTIVE (anonymous blob read, requires knowing blob URL).

Narrative

{container.name} in {account.name} is publicly readable from the internet without authentication: the account has allowBlobPublicAccess=true and the container publicAccess={container.properties.publicAccess}. Any internet user can read{if publicAccess=='Container': ' and list'} blobs in this container without any credential.

Raw rule rules/derived/azure/storage.yaml

id: azure-storage-public-blob-container
emits: ExposedToInternet
description: Storage account with public blob access enabled and a public container exposes blob data
  to unauthenticated internet access.
match:
- - account: null
  - Contains
  - container: null
where:
- node_class(?account) == Storage and node_type(?account) == ObjectStorage
- node_type(?account) matched provider_type == 'Microsoft.Storage/storageAccounts'
- ?account.properties.allowBlobPublicAccess == true
- node_type(?container) == ObjectStorage
- node_type(?container) matched provider_type == 'Microsoft.Storage/storageAccounts/blobServices/containers'
- ?container.properties.publicAccess in ['Blob', 'Container']
emit:
  source_type: ObjectStorage
  target_type: AnonymousIdentity
  source: ?container
  target: <AnonymousIdentity>
  permissions: []
  conditions: []
  state_logic: "if ?account.properties.publicNetworkAccess == Disabled: DO NOT EMIT (storage account explicitly\
    \ blocks all public network access regardless of blob public access settings). Elif ?account.properties.allowBlobPublicAccess\
    \ == false: DO NOT EMIT (container publicAccess is ignored by the storage service when account flag\
    \ is false). Elif networkAcls.defaultAction == Deny AND public internet IP not in IP allowlist: CONDITIONAL(network_reachability)\
    \ \u2014 the container property allows anonymous access but the network firewall blocks public internet\
    \ traffic to the storage endpoint. Elif publicNetworkAccess == Enabled AND networkAcls.defaultAction\
    \ == Allow: ACTIVE (no network_reachability condition \u2014 the endpoint is publicly accessible by\
    \ design). For publicAccess=Container: ACTIVE (anonymous list-and-read); for publicAccess=Blob: ACTIVE\
    \ (anonymous blob read, requires knowing blob URL)."
  confidence: '0.93'
  derived_from:
  - rule_id: azure-storage-account-contains-container
    binding: ?account Contains ?container
  - rule_id: azure-storage-container-public-access-property
    binding: ?container HasPolicy <publicAccess>
  - rule_id: azure-storage-allow-public-access-property
    binding: ?account HasPolicy <allowBlobPublicAccess>
  false_positive_note: "CRITICAL: ?account.properties.allowBlobPublicAccess == false OVERRIDES any container-level\
    \ publicAccess \u2014 the storage service returns 409 for anonymous requests on such accounts. Do\
    \ NOT emit ExposedToInternet when the account flag is false even if the container property says Blob\
    \ or Container. publicNetworkAccess=Disabled completely blocks public access regardless of container\
    \ publicAccess \u2192 do not emit. A network firewall (defaultAction=Deny with no public IP rule)\
    \ blocks the public endpoint \u2192 emit CONDITIONAL(network_reachability). CRITICAL FIX: do NOT include\
    \ network_reachability in conditions[] when the account is intentionally public (publicNetworkAccess=Enabled,\
    \ networkAcls.defaultAction=Allow). The endpoint is publicly reachable BY DESIGN; emit ACTIVE with\
    \ no conditions. publicAccess=Container allows directory listing (broader than Blob-only) \u2014 note\
    \ in narrative. This is anonymous data exposure; target AnonymousIdentity, not a tenant or subscription\
    \ boundary node. Azure Storage Accounts created after November 2023 default to allowBlobPublicAccess=false\
    \ \u2014 verify the actual property."
  narrative: '{container.name} in {account.name} is publicly readable from the internet without authentication:
    the account has allowBlobPublicAccess=true and the container publicAccess={container.properties.publicAccess}.
    Any internet user can read{if publicAccess==''Container'': '' and list''} blobs in this container
    without any credential.'
move · open · esc close