Each of the three big providers has a switch above the individual bucket that overrides whatever someone configured below it. Your afternoon has two jobs: find what is already open, then turn those switches on so the next careless policy does nothing. Do them in that order, because the switches will break anything that relies on being public.
What "public" means on each platform
The definitions differ, and the audit has to look for each one.
| Platform | What makes it public | Guardrail that overrides it |
|---|---|---|
| AWS S3 | ACLs granting access to the AllUsers or AuthenticatedUsers groups, or a bucket policy that is not provably non-public | Block Public Access, at account or organisation level |
| Azure Blob | Account allows anonymous access and the container level is set to Container or Blob | AllowBlobPublicAccess set to False on the storage account |
| Google Cloud Storage | allUsers or allAuthenticatedUsers in a bucket IAM policy or an object ACL | Public access prevention, per bucket or via the storage.publicAccessPrevention constraint |
S3 treats a bucket policy as public until proven otherwise. It counts as non-public only if it grants access to fixed values, with no wildcards or policy variables, such as a specific principal or a set of CIDR blocks via aws:SourceIp. A wildcard principal therefore fails the test.
On Azure, both conditions must hold. If the account disallows anonymous access, the container setting is irrelevant. If the account allows it, a container still stays private until someone sets its level to Container or Blob.
Step 1: find what is open
S3. The AWS guide points to IAM Access Analyzer for S3 for reviewing public buckets, so start there. Then check the four Block Public Access settings on each account: BlockPublicAcls, IgnorePublicAcls, BlockPublicPolicy and RestrictPublicBuckets. Note that GetBucketAcl returns the effective permissions. A bucket with a public ACL and IgnorePublicAcls enabled will show an ACL that hides the real one.
Azure. The portal gives you a count of anonymous requests first. In the storage account, open Monitoring, then Metrics. Use the Blob namespace, metric Transactions, aggregation Sum, and add a filter with Authentication equal to Anonymous.
To see which containers are involved, create a diagnostic setting that sends logs to a Log Analytics workspace. Then run this query, which covers the last seven days:
StorageBlobLogs
| where TimeGenerated > ago(7d) and AuthenticationType == "Anonymous"
| project TimeGenerated, AccountName, AuthenticationType, Uri
The Uri column names the containers and blobs being read. Metrics and logs only show who is reading anonymously, not what is exposed, so an open container nobody has requested stays invisible. Check container access levels as well.
Google Cloud. Inspect bucket IAM policies for roles granted to allUsers or allAuthenticatedUsers. Cloud Asset Inventory shows the policies for all buckets in a project, folder or organisation, which saves you going bucket by bucket. Object ACLs need a separate look, and managed folders have their own IAM policies. Turn on usage logs to see which workloads touch public data. Cloud Audit Logs does not track access to public objects, so it will not help here.
Step 2: switch on the guardrails
S3. AWS recommends enabling all four settings at account level. It also recommends all four on each bucket to satisfy Security Hub control S3.8. For several accounts, use an organisation-level policy. That policy is all-or-nothing, with no per-setting choice, and it overrides account-level settings in the selected member accounts. S3 applies the most restrictive combination across levels, so a bucket cannot weaken what the account enforces.
Apply BlockPublicPolicy at account level in particular. Anyone who can edit a bucket policy can otherwise insert a policy that disables the bucket-level setting. Account-level changes apply to all Regions but may not land everywhere at once. Access point settings can only be set at creation, so check those separately.
Azure. Set AllowBlobPublicAccess to False on each storage account. This overrides every container setting, so you do not need to touch the containers. It requires version 2019-04-01 or later of the Azure Storage resource provider. The user needs Microsoft.Storage/storageAccounts/write, which the Owner, Contributor and Storage Account Contributor roles carry. Keep those assignments narrow.
Google Cloud. Enforce public access prevention on buckets, or set the storage.publicAccessPrevention organisation policy constraint at project, folder or organisation level. Google advises enforcing at the highest level you can. A bucket's state is either enforced or inherited. A bucket with an enforced setting stays enforced even if you later disable the constraint above it.
What will break, and what will not
Plan for breakage, because that is the point of the exercise.
- S3: The settings do not change existing policies or ACLs. Removing a setting makes anything with a public policy or ACL public again, so clean up the underlying grants as well. Also review identity-based policies and resource policies such as KMS key policies, which Block Public Access does not cover.
- Azure: Anonymous reads start failing. The code depends on the service version: 401 for versions 2019-12-12 and newer, otherwise 409 when the account disallows anonymous access and 404 when it allows it.
- Google Cloud: Requests using
allUsersfail with HTTP 401 or 403. Existing bindings stay in place but are overridden. Attempts to add them fail with412 Precondition Failed. Signed URLs are unaffected. Enforcement can take up to 10 minutes, and cached objects can stay public for theCache-Controllifetime, which defaults to 3600 seconds.
Genuine public content, such as a static website, needs an exception. Azure advises moving it to a separate storage account reserved for anonymous access. On Google Cloud, disable prevention on the specific project that holds the bucket. In S3, customise the individual settings for the buckets that need it.
Where to start
- List every AWS account, Azure subscription and Google Cloud project that holds storage, including ones nobody remembers creating.
- Run the discovery above: Access Analyzer for S3, the Azure anonymous-request metric and log query, and Cloud Asset Inventory for bucket IAM policies.
- For each public finding, decide whether it is deliberate. Move legitimate public content into dedicated accounts or projects.
- Enable the account or organisation-level controls: all four S3 settings,
AllowBlobPublicAccessset to False, and thestorage.publicAccessPreventionconstraint. - Re-run the queries a few days later to confirm anonymous requests have dropped to zero, and set an alert on the Azure anonymous-request metric.
- If you want an outside check of what remains reachable, our penetration testing service covers cloud exposure, and our consulting team can review your account structure.
