A public bucket
Principal * on GetObject, which is how an S3 bucket becomes readable by the internet
{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":"*","Action":"s3:GetObject","Resource":"arn:aws:s3:::b/*"}]}Paste an S3 bucket policy and get the answer you came for first: can anyone on the internet read, write or list this bucket. Then the guardrails it is missing, and the ARN mistakes that make parts of it silently do nothing.
Or drop a file anywhere on this panel. Nothing is uploaded: the analysis runs in this tab.
The answer appears here
Paste on the left and press Check this policy. Nothing leaves this tab.
Nothing else to flag.
No formatting problems, and nothing the rules object to. Worth remembering what that covers: this reads the file you pasted, not the account or cluster it will be applied to.
No finding matches that filter.
Real input you can load into the tool above. Each one shows a different thing going wrong, because that is what the tool is for.
Principal * on GetObject, which is how an S3 bucket becomes readable by the internet
{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":"*","Action":"s3:GetObject","Resource":"arn:aws:s3:::b/*"}]}A policy that permits plaintext requests, because nothing denies aws:SecureTransport false
{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":"arn:aws:iam::123456789012:root"},"Action":"s3:GetObject","Resource":"arn:aws:s3:::b/*"}]}These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.
It grants to everyone on the internet, including unauthenticated requests. Almost every S3 leak is this line.
Instead:Use a specific principal, or CloudFront with origin access control for public content.
Without a Deny on aws:SecureTransport false, the bucket accepts HTTP.
Instead:Add an explicit Deny with that condition.
arn:aws:s3:::bucket covers bucket-level actions and arn:aws:s3:::bucket/* covers objects. ListBucket needs the first, GetObject the second.
Instead:List both, with the right actions on each.
Public S3 buckets are still one of the most common causes of a data breach, and almost never because someone decided to make a bucket public. They get there one statement at a time.
Before any list of findings, a straight verdict: can anyone on the internet read objects, write objects, list keys, or change the policy itself. Each is assessed separately, because public read and public write are very different problems and public list is what turns the first one from careless into catastrophic.
A wildcard principal narrowed by aws:PrincipalOrgID, aws:SourceArn or aws:SourceVpce is a normal pattern and is treated as such. An IP allowlist is not: aws:SourceIp does not apply when a request arrives over a VPC endpoint, so it is not a boundary and the tool says so.
S3 accepts plain HTTP unless a Deny on aws:SecureTransport says otherwise, and nothing tells you when a client uses it. Separately, requiring SSE-KMS on upload is what lets you name the key protecting the data, which is the question an auditor asks.
An anonymous grant on s3:PutBucketPolicy or s3:PutBucketAcl means whoever finds the bucket can rewrite its permissions. That makes every other restriction in the document advisory, so it is reported above everything else.
Object actions scoped to a bucket ARN with no /*, or s3:ListBucket scoped to object ARNs. Both are silently denied at runtime rather than rejected at save time, which is why they survive review.
A bucket policy is an IAM policy, so it also gets the wildcard grading, the escalation-path checks and the JSON validation from the IAM Policy Validator, including exact line and column on a syntax error.
A bucket with public read on objects is exposed, but only for keys somebody knows. Plenty of real buckets have lived that way for years because the keys are UUIDs and nothing links to them.
Add anonymous s3:ListBucket
and that changes completely. The keys are no longer secret, so every
object is reachable by anyone who finds the bucket name, and bucket
names are guessable because they follow company naming conventions.
This is the difference between the two findings, and why they are
reported separately rather than as one "bucket is public".
Public asset buckets essentially never need ListBucket. Clients fetch URLs the application already gave them.
A bucket policy is a resource policy: it is attached to the bucket and names who may do what to it. A bucket becomes public when that policy grants an action to a principal of "*" without a condition that meaningfully narrows it, and the word meaningfully is doing a lot of work, because several conditions that look like boundaries are not.
Not every AWS account, not authenticated users: anyone at all, with no credentials. A statement with that principal and no narrowing condition grants its actions to the world. Which actions matters as much as the fact: read, write, list and policy-control are four different problems, and public list combined with public read is worse than either alone because it turns a bucket you have to guess the keys of into a directory anyone can walk.
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/*"
}
anonymous read of every object: the standard public-website policy,
and a data breach if the bucket was not meant to be one Actions on the bucket itself, such as ListBucket, take the bucket ARN. Actions on objects, such as GetObject, take the ARN with a slash-star after it. Getting this wrong is a common and confusing failure: a policy granting GetObject on the bucket ARN grants nothing at all, and one granting ListBucket on the object ARN does the same. Both are valid JSON and neither warns you.
arn:aws:s3:::my-bucket the bucket -> s3:ListBucket
arn:aws:s3:::my-bucket/* the objects -> s3:GetObject The four Block Public Access settings are evaluated before the policy and can override it, at bucket level or account level. This is genuinely useful and it is also why a policy review alone is not the whole answer: a policy can look public and be blocked, or look safe while an account-level setting is off. Reading the file tells you what the policy grants; only the account tells you what is actually reachable.
aws:SourceIp looks like a restriction and is not a reliable one: an IP condition does not apply to requests arriving over a VPC endpoint, so it can be bypassed rather than merely narrowed. The CloudFront service principal without an aws:SourceArn condition is the subtler one, without it, any CloudFront distribution in any AWS account can read the bucket, and the policy reads as perfectly reasonable in review. Conditions that do narrow a wildcard properly are aws:PrincipalOrgID, aws:SourceVpce and aws:SourceArn.
"Condition": {
"StringEquals": { "AWS:SourceArn": "arn:aws:cloudfront::111122223333:distribution/E1234" }
}
without this line, every CloudFront distribution on earth qualifies