Skip to main content
cd ../config-traps
risk/register/s3-wildcard-principal-without-conditions-the-leak.html
AWS S3 Access Controlcritical severityAWS S3

S3 Wildcard Principal Without Conditions: The Leak

Severity
critical
Reviewed
12 Jul 2026
Remediation
~20 minutes
Overview

Deleting a Condition block to silence an AccessDenied error leaves Principal "*" unconstrained, so s3:GetObject or s3:PutObject becomes callable by any unauthenticated caller. Buckets with BlockPublicPolicy overridden to false accept the policy silently, and scanners fingerprint the exposure within hours.

Operational summary

At a glance

Symptom
Engineer strips Condition block to clear AccessDenied during testing, leaving wildcard principal unscoped
Likely cause
Bucket policy Principal "*" with no Condition, and BlockPublicPolicy overridden to false
Impact
Any unauthenticated internet caller gains read or write access to bucket objects, enabling exfiltration or ransomware-style overwrite
Verification signal
Re-run the detection test and a controlled negative-path test.
Safe correction
Restore scoped Condition using aws:SourceArn or aws:PrincipalOrgID; enforce account-wide Block Public Access; validate policies with Access Analyzer
Rollback or recovery
Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.

The Trap

Wildcard Principal (“Principal”: “*”) left in an S3 bucket policy with no accompanying Condition block. This is distinct from a plain public bucket toggle; the policy looks scoped because it references a specific service, action, or resource, but nothing actually constrains who that wildcard resolves to.

The Default State

AWS’s own documentation examples for CloudFront legacy OAI access, ALB/NLB access log delivery, and SES inbound receipt rules all ship with Principal: “*” paired with a Condition using aws:SourceArn or aws:SourceAccount. Engineers copy the Principal and Action blocks into Terraform’s aws_s3_bucket_policy resource or a CloudFormation template, then hit an AccessDenied error during testing. The fastest fix under deadline pressure is deleting the Condition block rather than diagnosing why the ARN or account ID doesn’t match — the policy applies cleanly once the constraint is gone, and the ticket closes as resolved.

The Blast Radius

S3 Block Public Access normally intercepts PutBucketPolicy calls that grant public access via Principal “*” unless the statement includes a recognised narrowing condition key — aws:SourceArn, aws:SourceIp, aws:SourceVpce, aws:PrincipalOrgID, or similar. But BlockPublicPolicy is bucket-overridable, and buckets provisioned before the 2023 account-level default, or via IaC modules that explicitly set BlockPublicPolicy=false for compatibility with older CloudFront setups, accept the policy without complaint. Once attached, s3:GetObject or s3:PutObject becomes callable by any unauthenticated request. Internet-wide scanners fingerprint newly created buckets from CloudTrail-adjacent DNS patterns and S3 naming conventions within hours; exposed write access invites ransomware-style overwrite of objects, exposed read access invites bulk exfiltration via aws s3 sync run anonymously with –no-sign-request.

The Lead Mechanic Fix

Restore the Condition and scope it to the actual caller, not the service name: “Condition”: {“StringEquals”: {“aws:PrincipalOrgID”: “o-xxxxxxxxxx”}, “ArnLike”: {“aws:SourceArn”: “arn:aws:cloudfront::123456789012:distribution/EDFDVBD6EXAMPLE”}}. Enforce account-wide guardrails with aws s3api put-public-access-block --bucket BUCKET --public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true and remove any bucket-level override that disables it. Before deployment, validate every candidate policy with aws accessanalyzer validate-policy --policy-document file://policy.json --policy-type RESOURCE_POLICY, which flags PUBLIC_S3_BUCKET findings distinct from cosmetic syntax errors. Run an IAM Access Analyzer external-access scan continuously against the account, and treat any finding with an empty condition set as a Sev-1, not a backlog item.

03

Apply the safer control

Before you change production

Confirm the affected scope, export the current configuration, and test the replacement control in a non-production environment first.

Fix commands and configuration

aws s3api put-public-access-block --bucket BUCKET --public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
aws accessanalyzer validate-policy --policy-document file://policy.json --policy-type RESOURCE_POLICY
04

Verify, roll back or escalate

Verify

Re-run the detection test and a controlled negative-path test. Confirm the unsafe behaviour is blocked while approved traffic still succeeds.

Rollback

Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.

Escalate

Escalate when the blast radius is uncertain, the control cannot be tested safely, or remediation requires an outage or security exception.

After remediation

Further reading stays below the corrective workflow and is selected by platform, category and shared technical keywords.

Discover more

Connected KBY resources