← Cloud Bucket Exposure Checker

How to fix a publicly listable bucket

If Cloud Bucket Exposure Checker (or any other check) just reported that an AWS S3, Google Cloud Storage, or Azure Blob Storage bucket allows anonymous public listing, the fix is a provider setting, not a file you delete -- and it is a different setting on each of the three. The order below closes the listing first, then checks the separate thing a clean listing result does not cover.

1. Block anonymous listing at the provider

Deleting the object you were worried about does nothing to the bucket's own listing permission -- the next anonymous request can still enumerate whatever is left. Fix the setting that actually controls it:

# AWS S3 -- block public access at the bucket (and prefer the account-level
# setting too, so a future bucket cannot be created public by default)
aws s3api put-public-access-block --bucket YOUR-BUCKET \
  --public-access-block-configuration \
  BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true

# Google Cloud Storage -- turn on Public Access Prevention, and remove any
# allUsers / allAuthenticatedUsers grant from the bucket's own IAM policy
gcloud storage buckets update gs://YOUR-BUCKET --public-access-prevention
gcloud storage buckets remove-iam-policy-binding gs://YOUR-BUCKET \
  --member=allUsers --role=roles/storage.objectViewer

# Azure Blob Storage -- set the container's public access level to Private
az storage container set-permission --name YOUR-CONTAINER \
  --account-name YOUR-ACCOUNT --public-access off

A Block Public Access setting or an explicit ACL/IAM grant can each cause the same symptom on AWS and GCS -- check both a bucket policy and the legacy ACL on S3, and both Public Access Prevention and the IAM policy on GCS. Azure's per-container public access level is the one setting that governs both listing and object read together, unlike S3 and GCS, where they are separate.

2. Re-run the checker to confirm the listing itself is closed

node check.mjs <bucket-url> again, against the same URL. A private or not-found result means the one request this tool ever sends -- the anonymous LIST -- is now refused or the bucket no longer resolves. That confirms listing is fixed. It does not confirm anything about whether an individual object is still publicly readable, because this tool was never designed to test that -- see step 3.

3. Check public object-read access separately

A bucket can have listing blocked while a direct link to a known object key still resolves publicly -- a real, separate exposure this checker cannot detect, because it never has an object key to test without a listing to find one in. Confirm it with the provider's own tooling instead of assuming a clean listing result covers it: AWS IAM Access Analyzer for S3 (flags buckets and objects reachable from outside the account), the Public access badge GCS shows per bucket in the console, or Azure's container-level public access setting, which -- unlike S3 and GCS -- already controls object read at the same time as listing, so fixing step 1 on Azure fixes both.

4. Find out how it became public, and fix that too

A bucket rarely goes public by itself. The common causes are a bucket policy or ACL added for a one-off integration and never removed, an infrastructure-as-code module whose default is public-read, or a manual console change nobody recorded. Check every other bucket the same account, team, or Terraform/CloudFormation module created for the same misconfiguration -- a fix applied to one bucket at a time is exactly how the next one gets missed -- and turn on account-level Block Public Access (S3) or org-level Public Access Prevention (GCS) so a newly created bucket cannot repeat it by default.