Object Storage Exposure Review: An Outside-In Method

Exposed object storage is rarely found first by its owners. Automated scanners enumerate bucket names around the clock, attempt unauthenticated reads, and index whatever answers. The reliable counter is to run the same outside-in object storage review on a fixed schedule: enumerate every storage container your organization owns, probe each one without credentials, and close what answers with the platform-level guardrail — S3 Block Public Access on AWS, Public Access Prevention on Google Cloud, or the account-level anonymous-access switch on Azure. On AWS, by default, new buckets, access points, and objects don’t allow public access, yet users can still modify bucket policies, access point policies, or object permissions to open data to the internet — which is exactly why a recurring external check matters more than a one-time configuration pass.
Why Outside-In Beats Console Checks
Consoles and inventory exports show what was configured; an unauthenticated request shows what the platform actually serves. The two diverge more often than teams expect: an access point carrying its own policy, a bucket policy edited during an incident, or a container created before current defaults can all serve objects while the dashboard still suggests a private posture. Attackers don’t read your configuration; they send requests and keep what answers. That asymmetry is the reason an external pass belongs in the same cycle as the configuration sweep — one finds intent, the other finds exposure. For the tooling behind the first half of this loop, the earlier walkthrough of object storage enumeration detection covers scanner behavior and logging signals in depth; this article stays on the review method that turns findings into closed tickets.
Enumerate Every Storage Container
Before any probing, the review needs a complete map, and completeness is the hard part. Missing a single shadow account invalidates every later step, because attackers enumerate without respect for your org chart. On Google Cloud, instead of fetching IAM policies bucket by bucket, you can use Cloud Asset Inventory to view the policies for all buckets in a project, folder, or organization, which turns a multi-day console crawl into a single export. The working order is always inventory first, probes second:
- Pull the account list from the organization or billing layer, never from team memory; shadow accounts are where exposure hides longest.
- Export every bucket, storage account and container through each platform’s inventory API rather than by clicking through consoles.
- Tag each container with owner and data class as you go — a finding without an owner is a finding nobody fixes.
- Flag resources created before secure defaults existed; older objects most often predate the guardrails described below.
Probe Each Endpoint Anonymously
The probe stage reproduces the attacker’s cheapest move: an unauthenticated request. Keep it simple and repeatable:
- Attempt an unauthenticated GET or HEAD against each container root and against a sample of known object keys.
- Record the HTTP status, not just pass or fail: a 200 is an exposure, a 403 confirms the object exists but stays protected, a 404 hides existence.
- Probe from outside your corporate network — VPN and private-link ranges can mask true public posture.
- After every fix, retest from the same vantage point and attach both results to the ticket as before-and-after evidence.
Treat the sample keys as your own hypothetical test set, chosen from prefixes you know exist; never present them as measured breach data. If a probe answers with a listing rather than a single object, raise severity immediately — enumerable prefixes let an attacker walk the entire dataset.
Close Findings With Guardrails
A finding is not closed when the console looks green; it is closed when the anonymous probe stops answering and a guardrail blocks regression. On AWS, the strongest regression control is account-wide: turn on all four Block Public Access settings for your account, as the S3 documentation recommends, so permissive policies and ACLs stop mattering no matter how the bucket was created. On Google Cloud, enforcing Public Access Prevention works at write time: requests that would add allUsers or allAuthenticatedUsers to a policy or ACL fail with 412 Precondition Failed, so future misconfigurations are rejected instead of discovered. On Azure, the equivalent control sits at the account level: with AllowBlobPublicAccess disabled, per Microsoft’s anonymous access guidance, disallowing anonymous access for the storage account overrides the anonymous-access settings of all containers in that account. Use this checklist to drive each platform’s pass:
| Platform | Inventory pull | Guardrail to verify | External probe |
|---|---|---|---|
| AWS S3 | List buckets per account | Block Public Access, account level, all settings on | Anonymous GET on container root and sample keys |
| Google Cloud Storage | Cloud Asset Inventory policy export | Public Access Prevention enforced or inherited | Anonymous GET on bucket URL |
| Azure Blob Storage | List storage accounts and containers | AllowBlobPublicAccess disabled | Anonymous GET on blob URL |
Set the Review Cadence
Run the probe pass monthly and the full inventory sweep at least quarterly, with event-driven reviews whenever a new account or region appears, a Terraform change touches storage resources, or an audit log shows a public-access permission being granted. Pair the external pass with the existing monthly exposure audit routine so configuration drift and effective exposure are checked in the same window. Keep every run’s results — inventory hash, probe statuses, fixes applied — attached to a dated ticket, so the next review starts from evidence instead of memory. The next step is concrete: take the table above as the per-platform checklist, run one full pass this week, and let the 200 answers, not the consoles, decide what gets fixed first.