Azure Storage accounts are where most Azure data exposures start, because a single account key or SAS URL can grant full access to every container without any user signing in. The service still accepts Shared Key authorisation and, on older or script-built accounts, anonymous blob reads and TLS 1.0 unless someone switches them off. This guide covers the storage-account settings that decide who can reach the data and how: Microsoft Entra ID versus Shared Key, the three SAS types, anonymous access, network rules, logging and immutability for ransomware resilience. It is for cloud security engineers, platform teams and auditors. For the wider platform see the Microsoft Azure Security Guide; for SAS design in depth see the Azure SAS Tokens Security Guide.
Key takeaways
- Turn off Shared Key. Setting
AllowSharedKeyAccessto false makes Entra ID the only way in for signed requests and kills account SAS and service SAS in one move; measure usage first because Azure Files and some tools still depend on keys. - Control-plane roles are data roles in disguise. Owner, Contributor and Storage Account Contributor include
listKeys/action, so anyone holding them can read every blob through the account key while Shared Key stays enabled. - Prefer user delegation SAS. It is signed with Entra credentials, capped at 7 days and revocable by revoking the delegation key; account SAS cannot be revoked except by rotating the key that signed it.
- Explicitly disallow anonymous access and old TLS. Accounts built from CLI, PowerShell or templates can leave
AllowBlobPublicAccessandMinimumTlsVersionunset; enforce both with Azure Policy in Deny mode. - Immutability and soft delete are the ransomware controls. A locked time-based retention policy stops even account administrators deleting or overwriting blobs; soft delete and versioning cover the cases immutability does not.
How identity and access work in Azure Storage
Every request to the blob, file, queue or table endpoint is authorised in one of four ways, and each storage account can accept all four at once unless you restrict it. Resource logs record which one was used in AuthenticationType.
| Method | What signs the request | Revocation | Recommended use |
|---|---|---|---|
| Microsoft Entra ID (OAuth 2.0) | Token for a user, service principal or managed identity, checked against Azure RBAC data actions | Remove the role assignment or disable the identity; propagation can take up to 30 minutes | Default for all workloads and people |
| Shared Key | One of two 512-bit account keys (key1, key2) | Regenerate the key; this breaks every client and SAS signed with it | Legacy only; disable where possible |
| Shared access signature (SAS) | User delegation key (Entra) or account key (service SAS and account SAS) | User delegation: revoke delegation keys. Service SAS: delete the stored access policy. Account SAS: rotate the key | Time-boxed delegated access for clients that cannot hold an Entra identity |
| Anonymous | Nothing | Disable anonymous access on the account | Only in a dedicated account for genuinely public content |
Two Azure RBAC planes apply. Management roles (Owner, Contributor, Storage Account Contributor, Reader) act on the account resource through Azure Resource Manager. Data roles carry data actions: Storage Blob Data Owner (adds POSIX ACL management for Data Lake Storage), Storage Blob Data Contributor (read, write, delete), Storage Blob Data Reader (read only) and Storage Blob Delegator (can request a user delegation key to create user delegation SAS). Equivalent data roles exist for queues, tables and files.
- Scope: assign data roles at the narrowest level that works: container, then storage account, then resource group. Microsoft warns that data permissions assigned at management group scope can in rare cases take up to 12 hours to apply, which makes that scope a poor fit for just-in-time elevation through Privileged Identity Management.
- Portal behaviour: if a user holds a role with
listkeys/action, the portal silently uses the account key rather than their Entra identity. Such users need a data role before Shared Key is disabled.
For how Azure RBAC and service principals work across the platform, see the Azure RBAC and Service Principals Guide.
Admin roles and privileged access
The highest-risk permission on a storage account is not a data role. It is Microsoft.Storage/storageAccounts/listkeys/action, held by Owner, Contributor, Storage Account Contributor and the Storage Account Key Operator Service Role. With Shared Key enabled, that one action is equivalent to full data access on every container, and the subsequent reads are signed with a key rather than an identity, so they do not show a user principal in the storage logs.
| Permission or role | What it really allows | Control |
|---|---|---|
listkeys/action (Owner, Contributor, Storage Account Contributor, Key Operator) | Read the account keys, therefore all data via Shared Key, and sign account SAS for any duration the account policy allows | Disable Shared Key; make these roles eligible-only in PIM; alert on the operation in the Activity log |
regeneratekey/action | Rotate keys, which breaks running applications and revokes key-signed SAS | Restrict to the platform team; include in the incident runbook as a revocation lever |
Microsoft.Storage/storageAccounts/write | Re-enable Shared Key, anonymous access or public network access; change TLS minimum; remove network rules | Azure Policy Deny assignments so the setting cannot be reverted; resource lock on production accounts |
| Storage Blob Delegator plus a data role | Mint user delegation SAS up to 7 days for anything the principal can access | Grant to the specific services that issue links, not to developers broadly |
- Just-in-time elevation: use PIM eligible assignments for Owner and Contributor on subscriptions that hold sensitive storage, with approval and a short activation window. See the Privileged Identity Management (PIM) Guide.
- Resource locks: an Azure Resource Manager CanNotDelete lock on the account blocks accidental or malicious account deletion and configuration change through the management plane. It does not protect the blobs.
Integrations, API tokens and service accounts
Most storage access is machine to machine: application back ends, data pipelines, backup agents, CI jobs, third-party analytics tools and partner uploads. These are non-human identities, and the account key and SAS URL are the most commonly leaked credentials in Azure. The Microsoft SAS token exposure of 2023 is the reference case: an account SAS with full control permissions, published in a public GitHub repository, with its expiry extended to 2051, exposed 38TB of internal data including workstation backups holding secrets.
Choosing the credential for each integration
- Azure-hosted workload: use a managed identity with a data role at container scope. No secret exists to leak or rotate. See the Azure Managed Identities Security Guide.
- Workload outside Azure (GitHub Actions, another cloud, Kubernetes): use workload identity federation on an app registration or user-assigned managed identity so the job exchanges its own OIDC token for an Entra token. Avoid client secrets; if one is unavoidable, follow the Azure Entra Client Secrets Security Guide.
- Browser or partner upload or download: issue a user delegation SAS from a back end that holds Storage Blob Delegator, scoped to one blob or container, the minimum permissions (for example create and write only, no list or delete), HTTPS only (
spr=https), a start time and an expiry measured in minutes or hours. - Legacy tool that only speaks Shared Key: isolate it in its own storage account, keep the key in Azure Key Vault and retrieve it at runtime, set a key expiration reminder, and plan replacement. See the Azure Key Vault Security Guide.
SAS guard rails at account level
- SAS expiration policy: sets an upper limit on the validity interval (expiry minus start) for SAS on the account. Action Log (the default) allows out-of-policy requests and records them in
SasExpiryStatus; Block rejects them. A SAS without a signed start is out of policy. The policy cannot be set for key-signed SAS until each key has been rotated at least once. - Stored access policies: if a service SAS must be used, bind it to a stored access policy so it can be revoked by deleting or editing the policy. Microsoft's guidance is that a service SAS without a stored access policy should expire in one hour or less because it cannot otherwise be revoked.
- Key expiration policy: sets a rotation reminder for account keys; the built-in policy Storage account keys should not be expired reports overdue accounts.
- Cross-tenant object replication: disallow it so replication policies cannot copy data to an account in another Entra tenant.
Block storage connection strings (AccountKey=) and SAS query strings (sig=) in secret scanning, and revoke findings rather than deleting them from history. The Microsoft Azure Storage exposure of 2024 shows the other half of the problem: an openly reachable storage server holding scripts and configuration files full of credentials for other systems.
Logging and detection
Storage has two log streams and both are needed. The Azure Activity log records control-plane operations (key listing, key regeneration, configuration changes, network rule changes, policy and lock changes) and is on by default. Resource logs for each service (blob, file, queue, table) record data-plane requests and must be enabled through a diagnostic setting, with the StorageRead, StorageWrite and StorageDelete categories sent to a Log Analytics workspace or your SIEM. See the Azure Monitor, Activity Log and Diagnostic Settings Guide for deployment at scale.
| Signal | Source and field | Why it matters |
|---|---|---|
| Account key listed | Activity log, operation Microsoft.Storage/storageAccounts/listKeys/action | Precedes Shared Key data access; unusual caller or time is a strong signal |
| Security setting changed | Activity log, storage account write with changed allowSharedKeyAccess, allowBlobPublicAccess, publicNetworkAccess or network rules | Reverting a baseline is a common preparation step for exfiltration |
| Authentication mix | StorageBlobLogs, AuthenticationType (OAuth, AccountKey, SAS, Anonymous) | Tracks Shared Key retirement and spots unexpected key or anonymous use |
| Out-of-policy SAS | StorageBlobLogs, SasExpiryStatus starting "Policy violated" | Finds long-lived tokens before switching the policy to Block |
| Bulk read, list or delete | StorageBlobLogs, OperationName such as ListBlobs, GetBlob, DeleteBlob, with CallerIpAddress and UserAgentHeader | Discovery and collection (ATT&CK T1619, T1530) or destruction (T1485) |
| Threat alerts | Microsoft Defender for Storage | Anomalous access, Tor and malicious IPs, suspicious SAS use, malware on upload |
Microsoft Defender for Storage analyses control-plane and data-plane telemetry without needing your diagnostic logs, is priced per storage account, and adds optional on-upload and on-demand malware scanning billed per GB scanned (default cap 10,000 GB per account per month, after which scanning stops until the cap resets). Enable it at subscription level; its alerts include suspicious use of over-permissive SAS, traffic that Entra sign-in logs never see. See the Microsoft Defender for Cloud Guide for plan choice.
Metrics that show the programme is working
- Shared Key share: percentage of production accounts with
allowSharedKeyAccessfalse (from Azure Resource Graph), target 100% outside documented exceptions. - Key-authorised requests: weekly count of AccountKey and key-signed SAS requests in
StorageBlobLogs, trending to zero. - Anonymous exposure: number of accounts where
allowBlobPublicAccessis true or null, target zero outside the public-content account. - Recoverability: percentage of business-critical containers with soft delete of at least 7 days, versioning, and a locked immutability policy where required.
Hardening baseline
Apply the baseline through Azure Policy at management group scope so it holds for every new account, and use Audit before Deny. Italic names are Microsoft built-in definitions.
| Setting | Target value | Enforce with | Watch out for |
|---|---|---|---|
Allow storage account key access (AllowSharedKeyAccess) | false | Built-in policy Storage accounts should prevent shared key access | Unset (null) means allowed. Azure Files in the portal, AzCopy for Files, Storage Explorer for Files and Cloud Shell file shares rely on keys |
Allow Blob anonymous access (AllowBlobPublicAccess) | false | Policy on allowBlobPublicAccess not equal to false, Deny | Static website $web is unaffected; put genuinely public content in its own account |
| Minimum TLS version | TLS1_2 | Policy on minimumTlsVersion, Deny | Portal default is TLS 1.2 but CLI, PowerShell and templates leave it unset, which permits TLS 1.0. TLS 1.3 is supported but cannot yet be the minimum |
| Public network access | Disabled with private endpoints, or selected networks only | Policy plus network rules | Up to 400 IP rules and 400 virtual network rules per account; firewall blocks Azure services unless trusted services or resource instance rules are allowed |
| SAS expiration policy | Upper limit set, action Block after a Log period | Storage accounts should have shared access signature (SAS) policies configured | User delegation SAS are capped at 7 days anyway |
| Blob and container soft delete | Enabled, at least 7 days | Built-in policies | Soft-deleted data is billed at the active rate |
| Versioning and immutability | Versioning on; locked time-based retention for backup and records containers | Account and container configuration | Immutability is incompatible with point-in-time restore and not supported with NFS 3.0 or SFTP |
| Defender for Storage | On at subscription level | Defender for Cloud plan or policy | Malware scanning cap and cost |
Immutability for backup and records data
Immutable storage puts blobs in a write once, read many state. A time-based retention policy blocks modification and deletion for 1 to 146,000 days, measured from each blob's creation time; a legal hold blocks them until explicitly cleared. Policies can be container-level (no versioning needed, supports Data Lake hierarchical namespace) or version-level (needs versioning, can be set at account, container or blob version). An unlocked policy can be shortened or deleted and is meant only for testing; a locked policy can only be extended, cannot be removed by any user including account administrators, and is the form assessed for SEC Rule 17a-4(f). Microsoft recommends locking within about 24 hours, and container-level locked policies allow at most five extensions. Enable blob soft delete before applying immutability. For ransomware specific to storage, see the Cloud Ransomware Guide and the Backup Security and Immutable Backup Guide.
Rolling it out
- Inventory: run Azure Resource Graph queries for
allowSharedKeyAccess,allowBlobPublicAccess,minimumTlsVersionandpublicNetworkAccessacross all subscriptions. Treat null as non-compliant. - Enable telemetry: deploy diagnostic settings for blob, file, queue and table logs to a central workspace, and switch on Defender for Storage.
- Measure for two to four weeks: group requests by
AuthenticationType,TlsVersionandSasExpiryStatus, with caller IP and user agent, and name an owner for every key, SAS and anonymous caller. - Assign policies in Audit: at management group scope, review compliance, and record exceptions with expiry dates.
- Migrate callers: move workloads to managed identities or federation, partners to user delegation SAS, and public content to a dedicated account.
- Pilot ring: disable Shared Key, anonymous access and old TLS on non-production accounts, then low-risk production accounts, watching for HTTP 403 (Shared Key), 401 (anonymous) and 400 (TLS) errors.
- Enforce: switch policies to Deny so the settings cannot be reverted; set the SAS policy action to Block.
- Roll back if needed: reset the property on the affected account and record a time-bound policy exemption, so the exception stays visible.
Small and large estates
A small organisation with a few accounts can change them in the portal and rely on Defender for Storage alerts. A large estate needs policy, a central log workspace and an exception register. Either way: telemetry first, enforcement last.
Framework mapping
| Control area | NIST SP 800-53 Rev. 5 | NIST CSF 2.0 | ATT&CK v20 technique addressed |
|---|---|---|---|
| Entra ID only, least-privilege data roles | AC-3, AC-6 | PR.AA-05 | T1078.004 Cloud Accounts |
| Key and SAS lifecycle, secret scanning | IA-5, SC-12 | PR.AA-05 | T1552.001 Credentials In Files |
| TLS 1.2 and secure transfer | SC-8 | PR.DS-02 | |
| Encryption at rest, customer-managed keys | SC-28 | PR.DS-01 | |
| Network rules and private endpoints | SC-7 | PR.IR-01 | T1530 Data from Cloud Storage |
| Policy-enforced baseline | CM-6 | PR.PS-01 | |
| Resource logs, Defender for Storage | AU-2, AU-6, SI-4 | DE.CM-09 | T1619 Cloud Storage Object Discovery |
| Soft delete, versioning, immutability | CP-9 | PR.DS-11 | T1485 Data Destruction, T1486 Data Encrypted for Impact, T1490 Inhibit System Recovery |
For Azure-specific control sets, see the Microsoft Cloud Security Benchmark Guide and the CIS Cloud Foundations Benchmarks Guide.
Common misconfigurations
- Account SAS as a sharing mechanism: a full-permission account SAS with a multi-decade expiry pasted into code or documentation. It cannot be revoked short of rotating the key, as the Microsoft SAS token exposure showed.
- Trusting the default: assuming new accounts are safe when they were created by Terraform, Bicep or the CLI, which leave the TLS minimum and other properties unset. Policy, not the portal default, is the control.
- Contributor everywhere: giving developers Contributor on a subscription "for convenience", which through
listKeyshands them every storage account's data and leaves their reads unattributed. - Disabling Shared Key without measuring: breaking Azure Files mounts, backup tools or Cloud Shell, then re-enabling it permanently instead of fixing the callers.
- Unlocked immutability: leaving retention policies in the unlocked test state, where an attacker with the right role can delete the policy and then the data.
- No data-plane logs: relying on the Activity log alone, which shows a key was listed but not what was read with it.
Practitioner checklist
- Query every subscription for
allowSharedKeyAccess,allowBlobPublicAccess,minimumTlsVersionandpublicNetworkAccess; treat null as non-compliant. - Send blob, file, queue and table resource logs to a central workspace and alert on
listKeysand security-setting changes in the Activity log. - Move workloads to managed identities or workload identity federation with container-scoped data roles.
- Replace account and service SAS with short-lived user delegation SAS issued by a back end.
- Assign the storage policies in Audit, then Deny, at management group scope, with time-bound exemptions.
- Enable soft delete (7 days or more), versioning and locked immutability on backup and records containers.
- Enable Defender for Storage at subscription level and decide on malware scanning per account.
- Rehearse revocation: rotate a key, revoke user delegation keys and delete a stored access policy in a test account.
Standards and references
- Microsoft Learn: Security recommendations for Blob storage
- Microsoft Learn: Prevent Shared Key authorization for an Azure Storage account
- Microsoft Learn: Remediate anonymous read access to blob data
- Microsoft Learn: Configure an expiration policy for shared access signatures
- Microsoft Learn: Store business-critical blob data with immutable storage
- Microsoft Learn: Authorize access to blobs using Microsoft Entra ID
- Microsoft Learn: Overview of Microsoft Defender for Storage
- NIST: SP 800-53 Rev. 5
Related NHI Mgmt Group resources: Azure SAS Tokens Security Guide · Azure Managed Identities Security Guide · Cloud Storage Exposure Playbook · T1530 Data from Cloud Storage Defensive Guide