All Articles
SecurityCloudPost-Mortem

How a Single Misconfigured Blob Exposed 2M Records

One wrong access setting on one storage container is all it takes to become a breach headline. Here's what actually happens, and how to stop it.

MGMohamed Ghassen BrahimMarch 18, 20268 min read

One wrong access setting on one storage container is all it takes to become a breach headline. Not a sophisticated zero-day. Not an APT group. A checkbox. A toggle. An anonymous access setting left on in a staging environment that somehow made it to production.

I've seen this happen twice in enterprise environments I've been brought into post-incident. The pattern is almost identical both times: a developer creates a blob container during a sprint, enables public access for convenience while building a feature, and then either forgets to lock it down or assumes someone else will. Nobody does. The container sits there, indexed by cloud asset crawlers within 24 to 48 hours, and the data walks out the door silently — without triggering a single alert.

Two million records. Customer names, emails, in one case partial payment data. Discovered not by an internal security team, but by a security researcher who found it via a public OSINT tool and sent a responsible disclosure email.

72hrs
Average time to indexing
Public cloud blobs indexed by crawlers post-creation
80%
Breaches involve misconfig
Not advanced exploits — human error at the control plane
€4.5M
Avg. EU breach cost
GDPR fines + notification + remediation (IBM 2024)
0
Alerts fired
In both incidents I reviewed — misconfiguration is silent

What Actually Happened

The incidents I'm describing are composites of real post-mortems, details changed to protect clients. But the anatomy is real.

The development team was building a document export feature. They needed a place to stage PDFs for user download. Someone created an Azure Blob Storage container, set access to "Blob" (anonymous read access for individual blobs), and passed the URL to the frontend team. The feature shipped. The container was promoted to production with the same settings, because nobody audited storage account configurations during the release process.

The container held export files keyed by a sequential integer ID. An attacker — or in this case, a researcher — simply iterated the IDs. export-1.pdf, export-2.pdf, all the way to export-2000000.pdf. No authentication required. The Azure portal, by default, does not surface this in any operational dashboard. Cost Management shows you spend on storage. It does not tell you your storage is world-readable.

The exposure chain follows the same pattern every time:

The second incident was a data lake. Parquet files containing enriched customer profiles, used by an analytics team. The container-level access policy had been set to allow anonymous access during a Proof of Concept, six months earlier. The PoC was decommissioned. The data was not.

⚠️

Anonymous access is not a testing shortcut — it's a liability

Azure Blob Storage has three access levels: Private, Blob (anonymous read for individual blobs), and Container (anonymous read + list). "Blob" sounds safer than "Container" but it provides near-equivalent exposure if your filenames are predictable, sequential, or guessable — which most application-generated filenames are.

The Five Conditions That Make This Possible

This kind of breach doesn't happen in isolation. It requires a confluence of failures — none individually catastrophic, but together they create the gap.

ConditionWhat It Looks LikeThe Fix
No policy baselineDevelopers can create storage accounts with any access levelAzure Policy: deny public blob access at subscription level
No IaC enforcementStorage containers provisioned ad-hoc via portal or CLI, not reviewedRequire all storage via Terraform/Bicep with PR review
No configuration drift detectionNobody checks if production matches the declared secure stateDefender for Cloud, Azure Policy Compliance dashboard
No data classificationTeams don't know what's in the container they're creatingData classification at ingestion — tag before storing
No egress monitoringMass downloads from a storage account don't trigger alertsAzure Monitor metric alerts on egress anomalies

Every one of these controls is available, and most are either free or near-free to implement. The gap is not tooling — it's the assumption that the defaults are safe.

They are not. Azure Blob Storage defaults to allowing anonymous access to be configured. The responsibility for locking it down sits with you.

How Attackers Find These Containers

If you think an exposed container is difficult to discover, the tooling that attackers use to surface them is sobering.

Cloud asset crawlers index publicly accessible cloud storage continuously. Services like Grayhat Warfare, OSINT tools built on Azure's documented API surfaces, and dedicated cloud exposure scanners run 24/7. A new public container is typically indexed within 24 to 72 hours of creation.

Certificate Transparency logs reveal new domain registrations and subdomains. If your storage account URL follows a predictable naming convention — yourcompanyname-prod-storage.blob.core.windows.net — it will be found.

Shodan and Censys index cloud endpoints. A storage account with anonymous access enabled will appear in their results.

Google dorking still works. site:blob.core.windows.net combined with your company name surfaces publicly accessible Azure blob containers via ordinary web search.

You do not need to be targeted. You just need to be exposed. The crawlers are not selective.

🔍

The disclosure gap is longer than you think

In both post-mortems I reviewed, the data had been accessible for more than four months before disclosure. During that time, multiple automated crawlers had indexed the container. The organisation had no way of knowing how many entities had downloaded the data, or whether it had already been sold. This is why prevention is categorically more valuable than detection in this class of incident.

The Remediation Sequence

When I'm brought in after this kind of incident — or when a client wants to close this class of risk before it happens — I run the same sequence.

Disable public blob access at subscription levelDay 1

In Azure Policy, assign the built-in policy "Azure Storage accounts should prevent shared key access" and separately enforce "Storage account public access should be disallowed." These policies can be set to Deny, not just Audit — which means new non-compliant storage accounts simply cannot be created. Apply to all production and staging subscriptions immediately. This is the single highest-leverage control.

Audit every existing storage accountDays 1–3

Run an Azure Resource Graph query across all subscriptions to list every storage account and its allowBlobPublicAccess setting. Any account with this set to true is a finding. For each: assess what data it contains, disable anonymous access, and determine whether authenticated access (SAS tokens, Azure AD with RBAC) is needed or whether the account can be locked to private. Do not wait for a full risk assessment before disabling anonymous access — disable first, investigate second.

Rotate all SAS tokens and shared access credentialsDays 3–5

If anonymous access was the ingress vector, SAS tokens and storage account keys are secondary concerns. But both post-mortems I reviewed surfaced keys stored in application settings and hardcoded connection strings. Rotate all storage keys. Audit Key Vault and application configuration for hardcoded credentials. Migrate to Azure AD-authenticated storage access (Managed Identities, Workload Identity) where possible.

Enable egress anomaly alertingWeek 2

Create Azure Monitor metric alerts on storage account egress bytes. A baseline of 500 MB/day with an alert threshold at 5 GB/hour is a starting point — calibrate to your actual workloads. For sensitive data stores, Defender for Storage provides automated anomaly detection including alerts for "unusual data access patterns" and "access from a Tor exit node." Enable it. The cost is negligible relative to a single breach notification exercise.

Implement IaC-only storage provisioningWeeks 2–4

Require all storage account creation to go through reviewed Terraform or Bicep templates. These templates should encode the secure-by-default settings: allowBlobPublicAccess = false, minimumTlsVersion = TLS1_2, supportsHttpsTrafficOnly = true. A portal-created storage account should trigger a non-compliance alert in your Defender for Cloud dashboard within minutes. Over time, move toward policy enforcement that blocks portal-originated storage creation entirely in production subscriptions.

The Notification Question

Once you've confirmed a breach of personal data, you are inside a regulatory clock in the EU. Under GDPR Article 33, you have 72 hours from becoming aware of a breach to notify your supervisory authority — not from the moment the data was exposed, but from the moment you have reasonable belief that a breach occurred. Under Article 34, high-risk breaches may also require individual notification to affected data subjects.

"Becoming aware" is interpreted broadly. A security researcher disclosure, an automated alert, a suspicious egress spike — all of these can start the clock. The 72-hour window is not a lot of time to simultaneously investigate, quantify, classify, and draft a compliant notification.

I've seen organisations lose days on the internal debate about whether the event qualifies as a "breach." It almost always does, if personal data was accessible. The correct posture is to notify, not to deliberate. A voluntary early notification with incomplete information is received better by regulators than a late notification that appears calculated.

Have a breach response runbook before you need it. Know who your DPO is. Know which authority you notify (in Germany, for example, that's the state-level Datenschutzbehörde in the state where your European establishment is located). Have notification templates drafted.

The Uncomfortable Truth

The reason these incidents keep happening is not ignorance. Every engineering team I've worked with knows that public storage is bad. The problem is that cloud platforms make the dangerous path frictionless and the secure path requires active effort.

Creating an anonymous storage account takes 30 seconds. Creating a storage account with correct RBAC, private access, diagnostic logging, lifecycle management, and alerting takes three hours if you do it carefully in Terraform. In a sprint, under pressure, that asymmetry matters.

The solution is to invert the asymmetry with policy and automation. Make the insecure configuration impossible — not just discouraged. Azure Policy's Deny effect is the mechanism. Use it.


If your organisation is storing sensitive data in Azure and you're not certain your storage accounts are locked down correctly, let's talk — a focused cloud security configuration review is the fastest way to find this class of risk before a crawler does.

Ready to act

Ready to put this into practice?

I help companies implement the strategies discussed here. Book a free 30-minute discovery call.

Schedule a Free Call