Azure Storage Security Deep Dive: SAS, OAuth, Firewalls, and Immutable Data · CloudEngineerHub
Security10 min read5 views
Azure Storage Security Deep Dive: SAS, OAuth, Firewalls, and Immutable Data
Harden Azure Storage end to end: shared key vs SAS vs Entra OAuth, firewall and private endpoint lockdown, and WORM immutability for ransomware defense — with working CLI and PowerShell.
A
Anuradha Kumari
Published: August 21, 2026· Last Updated: September 24, 2026·10 min read
Azure Storage Security Deep Dive: SAS, OAuth, Firewalls, and Immutable Data
Storage accounts hold the crown jewels — backups, exports, logs, and increasingly sensitive data lakes. Yet default configurations leave four doors open: account keys, over-broad SAS tokens, public network access, and mutable data that ransomware can encrypt or delete. Close all four methodically, in that order, because each layer makes the next one meaningful.
Visual Walkthrough
A bulletproof storage account stacks identity, network, data and monitoring.
Layered StackStorage Account Hardening — Four Defensive Layers5/5
PerimeterCore asset
1
Identity layer
Disable shared key; Entra OAuth + RBAC on data plane.
layer 1
2
Network layer
Private endpoints + firewall rules; public access off.
Defender for Storage, diagnostic logs, anomaly alerts.
layer 4
5
Assume-breach posture
SAS tokens minimized; every layer assumes the next one fails.
posture
Each layer assumes the previous one failed — depth is the strategy.
Storage Account Hardening — Four Defensive Layers. 1. Identity layer: Disable shared key; Entra OAuth + RBAC on data plane. — 2. Network layer: Private endpoints + firewall rules; public access off. — 3. Data layer: Encryption, immutable blobs (WORM), soft delete + versioning. — 4. Monitoring layer: Defender for Storage, diagnostic logs, anomaly alerts. — 5. Assume-breach posture: SAS tokens minimized; every layer assumes the next one fails.
A
Anuradha Kumari
Author
Endpoint management specialist focused on Microsoft Intune, Windows Autopilot and modern EUC. Writes practical deployment, compliance and troubleshooting guides for IT teams.
Roles — Storage Account Contributor (control plane) and a blob data role (Storage Blob Data Contributor) for data-plane validation.
Entra prerequisites — a managed identity or a service principal for application migration off shared key, plus enough Entra ID P1 for Conditional Access on user access.
Network inputs — the list of VNets/on-premises CIDRs that legitimately need access; DNS control to host privatelink zones if you go private endpoint.
Defender for Cloud — the storage plan enabled for malware scanning and sensitive-data threat alerts; it is the detection half of this hardening story.
A test application — something that reads a blob, so you can prove each lockdown step before it hits production.
Architecture: Four Independent Gates
Storage security is a stack of independent gates, and each must be closed for the others to matter:
Identity gate — who can touch the data plane: shared key, SAS, or Microsoft Entra OAuth.
Network gate — from where: public internet, selected VNets via service endpoints, or private endpoints.
Data gate — what the data can do after landing: versioning, soft delete, immutability (WORM).
Detection gate — who is watching: Defender for Cloud storage plan, activity logs, diagnostic settings.
A valid SAS does not bypass a Deny firewall. A private endpoint does not protect against someone with the account key. That independence is the design principle: a single locked door is theater; four layered gates are security.
Identity First: Shared Key, SAS, and Microsoft Entra OAuth
Three auth models coexist. Shared key (the account keys) is god-mode: it bypasses every data-plane RBAC check and is why Microsoft now lets you disable it. SAS tokens come in three flavors — service SAS, account SAS, and the user delegation SAS, which is signed with Microsoft Entra credentials instead of an account key and is the only SAS that survives key rotation cleanly. OAuth via Entra ID should be the default for applications, using DefaultAzureCredential and managed identity.
Bash
1
2
3
4
5
6
7
8
9
10
11
# Harden the account baseline
az storage account update -g rg-web -n stwebprod \
--min-tls-version TLS1_2 \
--allow-shared-key-access false \
--allow-blob-public-access false
# Hand out a short-lived, Entra-signed user delegation SAS
az storage blob generate-sas --account-name stwebprod \
--container-name exports --name report.csv \
--permissions r --expiry2026-01-01T10:30Z \
--auth-mode login --as-user --https-only
After disabling shared key access, applications failing with 403 AuthorizationFailure are almost always still using connection strings — migrate them to managed identity. Keep any SAS short-lived and, where revocation matters, bind it to a stored access policy you can kill instantly.
The PowerShell path for the same lockdown, including a user delegation SAS from a connected identity:
PowerShell
1
2
3
4
5
6
7
8
9
10
11
Connect-AzAccount
# Baseline hardening
Set-AzStorageAccount -ResourceGroupName rg-web -Name stwebprod -MinTlsVersion TLS1_2 -AllowSharedKeyAccess$false -AllowBlobPublicAccess$false# Expected output: the updated account object; ProvisioningState : Succeeded$ctx = New-AzStorageContext -StorageAccountName stwebprod -UseConnectedAccount# Generate a 1-hour read-only user delegation SAS (signed by Entra credentials, not the account key)
New-AzStorageBlobSASToken -Container exports -Blob report.csv -Permission r -Protocol HttpsOnly -ExpiryTime (Get-Date).AddHours(1) -Context$ctx -FullUri# Expected output: https://stwebprod.blob.core.windows.net/exports/report.csv?sv=...&sig=...
Network Lockdown: Firewalls and Private Endpoints
Set the storage firewall default action to Deny, then re-admit traffic either through service endpoints for selected VNets or through a private endpoint with a privatelink.blob.core.windows.net DNS zone. Remember the two gates: authentication (SAS, OAuth, keys) and network placement are independent — a perfectly valid SAS does not bypass a Deny firewall.
PowerShell equivalent for the public-access switch:
PowerShell
1
2
3
4
5
6
7
Set-AzStorageAccount -ResourceGroupName rg-web -Name stwebprod -PublicNetworkAccess Disabled
# Expected output: account object with PublicNetworkAccess : Disabled# Confirm the network rule set actually took
(Get-AzStorageAccount -ResourceGroupName rg-web -Name stwebprod).NetworkRuleSet |
Select-Object DefaultAction, Bypass
# Expected output: DefaultAction : Deny, Bypass : AzureServices (keep AzureServices so logs/backup still work)
Verify hybrid DNS from a client: Resolve-DnsName stwebprod.blob.core.windows.net should return a private 10.x address, never a public one.
Step-by-Step: A Hardening Sequence That Does Not Break Apps
Inventory consumers first: diagnostic settings, backup vaults, and Azure services are the trusted-AzureServices exceptions you must keep.
Set min-tls-version TLS1_2 and allow-blob-public-access false — safe by default, rarely breaking.
Turn on Defender for Cloud's storage plan so detections exist before restrictions do.
Migrate applications to managed identity one at a time; each migration is a deploy, not a config edit.
Set the firewall default action to Deny with AzureServices bypass; fix what fails, then remove the bypass if policy demands.
Add private endpoints + privatelink DNS for hybrid consumers; disable public network access last.
Disable shared key access (allow-shared-key-access false) and set a 90-day key rotation reminder for the break-glass keys that remain.
Add immutability, soft delete, and versioning on the containers that hold backups and audit data.
Configuration Deep Dive: SAS Flavors and WORM
Service SAS — one blob/container, one service; scope tight, sign short.
Account SAS — multiple services; treat as legacy and phase out — too much reach.
User delegation SAS — signed by Entra credentials; the only one that dies when you revoke the principal's access, which is a feature. Prefer it whenever SAS is unavoidable.
Stored access policy — attach to a service SAS so revocation is a policy delete, not key rotation.
Immutable storage — time-based retention policies and legal holds (WORM). Once covered, blobs cannot be modified or deleted until the policy expires or the hold releases, even by subscription Owners. A locked policy can be extended but never shortened — exactly what auditors and cyber-insurers want to hear.
Bash
1
2
3
4
5
az storage container immutability-policy create -g rg-web \
--account-name stwebprod --container-name audit-logs --period365
az storage container legal-hold set -g rg-web \
--account-name stwebprod --container-name audit-logs --tags litigation-2026
Run the policy Unlocked while validating retention lengths, then lock. Combine immutability with soft delete and versioning for layered ransomware defense.
Real-World Enterprise Scenario
A healthcare analytics firm stored monthly claims exports in blob storage. An external penetration test — with a leaked connection string from a decommissioned VM — reached the exports container within minutes. The remediation ran in one sprint: all four VMs consuming the account moved to user-assigned managed identity; the firewall went default-deny with a private endpoint for the hybrid ETL; shared key access was disabled, revoking the leaked string's power entirely; and the exports container gained a 180-day immutable retention policy plus versioning. The retest failed to reach anything. The pattern worth copying: the connection string was never "revoked" — the entire authentication model that made it valuable was switched off.
Troubleshooting
Symptom
Likely Cause
Fix
403 AuthorizationFailure after lockdown
App still authenticating with the disabled shared key
Migrate the app to managed identity / DefaultAzureCredential
Valid SAS suddenly denied
Storage firewall Deny; SAS cannot override network rules
Add the client's network path (service endpoint or private endpoint)
On-premises resolves storage to a public IP
privatelink DNS zone not linked/forwarded
Link the zone to the VNet; forward from on-prem DNS; re-test with Resolve-DnsName
Private endpoint stuck in "Pending"
Manual approval required on the storage account
Approve the private endpoint connection on the account's Networking blade
Backup or diagnostic data stops flowing
Default action Deny without AzureServices bypass
Set Bypass to AzureServices or add the dependent service's exceptions
Immutable container rejects a delete during legal hold
WORM is doing its job
Remove the hold only through legal process; never "fix" by policy delete
Visual Walkthrough
The five-step evaluation every role assignment request passes.
Decision TreeAzure RBAC Evaluation — Union, then Deny5/5
Decision gate
Action attempted
RBAC is additive (union) until a deny assignment appears.
Assignment matched
1
Scope walk
Check assignments from resource upward: RG → subscription → MG.
2
Union of roles
All matching assignments combine — multiple roles add up.
3
PIM elevation
Eligible roles can be activated for just-in-time access.
4
Action allowed
Data/action permission verified against the role definition.
Deny or none
1
Deny assignment check
Deny assignments always win — read them first.
2
No assignment found
Scope walk came up empty; access denied by default.
3
Request elevation
PIM request or assignment via owner — never widen broadly.
4
Least privilege wins
Smallest sufficient scope is the durable answer.
Azure RBAC Evaluation — Union, then Deny. Gate: Action attempted — Assignment matched: Scope walk — Assignment matched: Union of roles — Assignment matched: PIM elevation — Assignment matched: Action allowed — Deny or none: Deny assignment check — Deny or none: No assignment found — Deny or none: Request elevation — Deny or none: Least privilege wins
Common Errors
AuthorizationFailure (403) — almost always auth-model mismatch after a lockdown step; check which gate the client is failing, not just that it failed.
PublicAccessNotPermitted — blob public access disabled at account level; an app relying on anonymous reads needs identity.
MustUsePrivateEndpoint / NetworkRuleSet violation — the Deny firewall rejected a non-exempted source; add the correct network path.
ImmutabilityPolicyCannotBeShortened — locked retention can only extend; plan retention lengths before locking.
KeyRotationBlockedByImmutableContainer — some legacy clients regenerate SAS from keys; user delegation SAS sidesteps the dependency.
Best Practices
Treat account keys as break-glass: set allow-shared-key-access false and move apps to managed identity / DefaultAzureCredential.
Prefer user delegation SAS (Entra-signed) and short TTLs; stored access policies give instant revocation.
Auth and networking are separate gates — test each independently and document which gate a consumer uses.
Pair private endpoints with privatelink DNS zones and verify resolution from on-premises clients before cutting over.
Use immutable storage (locked retention + legal holds) on backup and audit containers; it is the control ransomware cannot bypass.
Security Considerations
Enable Microsoft Defender for Cloud's storage plan: malware scanning on uploads and anomaly detections catch what firewalls cannot.
Versioning plus soft delete turns "encrypted everything" into "restore last good version" — enable both before immutability, not after an incident.
Keep the AzureServices bypass deliberate: it is required for many platform flows but is also a documented exception for auditors.
Rotate keys on a schedule even when shared key is disabled — break-glass keys exist and should be treated as secrets with lifetimes.
Diagnostic settings to a Log Analytics workspace give you the data-plane audit trail; without it, a data exfiltration via OAuth is invisible.
FAQ
Can I keep SAS and still disable shared key?
Yes — user delegation SAS is signed by Entra credentials and keeps working when allow-shared-key-access is false. Account and service SAS signed from keys stop working.
What breaks when I disable shared key access?
Anything still using connection strings or account keys: old AzCopy versions, legacy SDKs, some backup agents. Inventory consumers first; the fix is managed identity in nearly every case.
Service endpoint or private endpoint for storage?
Service endpoints if consumers are Azure-only and DNS gymnastics are unwelcome; private endpoints when on-premises must reach storage privately or public network access must die completely.
Does immutability protect against an attacker with Owner?
Yes — a locked time-based retention policy or legal hold prevents deletion and modification even for subscription Owners. That is the entire point of WORM for ransomware defense.
How long should a SAS live?
Minutes to hours. If a consumer needs "long-lived SAS", it needs an identity instead — a managed identity with a scoped data-plane role.
Conclusion
Storage hardening is four gates in sequence: identity (kill shared key, prefer OAuth), network (default-deny, private endpoints), data (versioning, soft delete, WORM), and detection (Defender for Cloud). Each gate is simple; the discipline is refusing to declare victory after the first one.
Comments
0No comments yet — be the first to share your thoughts.