AWS S3 bucket left public? Here's the real fix
Your S3 bucket got exposed because of wrong bucket policy or ACL. Here's how to lock it down fast.
1. Bucket policy with a wildcard Principal
This is the #1 reason I see S3 buckets go public. Someone copies a policy from a blog post or old tutorial and leaves the Principal set to "*" or "AWS": "*". That's basically saying "everyone on the internet, come on in." I had a client last month whose entire customer database was exposed for three days because a dev used a snippet from Stack Overflow without checking the Principal.
The fix is straightforward. Open the S3 console, select your bucket, go to Permissions > Bucket Policy. Look for this:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::your-bucket-name/*"
}]
}
If you see a wildcard Principal paired with s3:GetObject, your bucket is public. The real fix is to either delete that statement entirely (if you don't need public access at all) or replace "*" with the specific AWS account ARN or IAM role that needs access. Don't use "AWS": "*" either — that's the same thing.
After you fix the policy, also check if you accidentally added s3:ListBucket to the same statement. That lets people see all your object names — sometimes worse than reading the content. Remove ListBucket unless you specifically need anonymous directory listing.
2. Public ACL on the bucket or its objects
Bucket ACLs are older than bucket policies but still bite people. Some AWS services (like CloudFront or Elastic Beanstalk) still set ACLs to public by default in certain configurations. I've seen this happen when someone uses the AWS CLI aws s3 cp with the --acl public-read flag and forgets to remove it later.
Check the ACL in Permissions > Access Control List. If you see Everyone (public access) with Read or Write permissions, that's the problem. The fastest fix is to click Block public access under the Permissions tab. But that only works if you haven't already set a bucket policy that allows public access — if you have, Block Public Access won't override it.
If Block Public Access is grayed out or says "Partially blocked," go back to the bucket policy first. Once the policy is clean, then turn on all four Block Public Access settings. This is a good practice for any bucket that doesn't need to serve public content.
For existing objects that got uploaded with public ACLs, you can use the AWS CLI to batch fix them:
aws s3api put-object-acl --bucket your-bucket-name --key path/to/object --acl bucket-owner-full-control
But don't do that one at a time. Use aws s3 cp with the --acl bucket-owner-full-control flag to re-copy everything:
aws s3 cp s3://your-bucket-name/ s3://your-bucket-name/ --recursive --acl bucket-owner-full-control
That'll take a while for big buckets but it's the only sure way to kill old public ACLs on individual objects.
3. Public access granted through cross-account or service-linked roles
This one's trickier. Sometimes a bucket isn't public in the obvious ways, but a service like AWS Lambda or EC2 has a role that grants s3:PutObject or s3:GetObject to an external account. I had a case where a company gave a vendor's AWS account access to an S3 bucket via a bucket policy, and the vendor accidentally made their own role public. That exposed the data through the vendor's side.
Check your bucket policy for statements where the Principal is an AWS account ID that isn't your own. For example:
"Principal": {
"AWS": "arn:aws:iam::123456789012:root"
}
If you see an external account ID, ask yourself: does this account really need access? If not, remove that statement. If yes, double-check that the external account's IAM roles and users are locked down.
Another thing to watch: S3 Object Lambda or S3 Access Points can create public access paths without showing up in the main bucket policy. Go to Permissions > Access Points and check each one's policy. I've found Access Points that had "Principal": "*" and no one remembered creating them.
Finally, use AWS Trusted Advisor or the S3 Security Check tool in the console. It'll scan all your buckets and flag any that are publicly accessible. It's not perfect — it won't catch the cross-account scenario — but it's a good first pass.
Quick-reference summary table
| Cause | Where to check | Fix |
|---|---|---|
| Bucket policy with wildcard Principal | Permissions > Bucket Policy | Remove Principal: "*" or replace with specific ARN |
| Public ACL on bucket or objects | Permissions > ACL | Block Public Access + re-copy objects with private ACL |
| Cross-account or service role access | Bucket policy + Access Points + Trusted Advisor | Remove external Principal or restrict role permissions |
Was this solution helpful?