CloudFront Origin Access Misconfig: Hitting the Bucket Directly
Teams put CloudFront in front of S3 and assume the bucket is private because the website URL says so. The CDN can be locked down while the origin still answers anonymous requests. That mismatch is the bug.
Your proof is simple: the same object key that should require CloudFront signed URLs or cookies is readable from the S3 website or REST endpoint.
Find the origin
Response headers help. x-amz-id-2, x-amz-request-id, or error XML shaped like S3 reveal the backend. DNS and certificate SANs sometimes name bucket.s3.amazonaws.com variants. Public Terraform may show the bucket id beside the distribution id. View-source on HTML that references absolute S3 hosts is still underrated recon.
Candidates to request directly:
https://BUCKET.s3.REGION.amazonaws.com/keyhttps://BUCKET.s3.amazonaws.com/key- Legacy website endpoints if static hosting is enabled
Use object keys you observed through the CloudFront URL on pages you are allowed to browse—product images, JS bundles, or a canary you uploaded if write access exists elsewhere. Do not brute-force private key spaces. Guessing /backups/ on a media bucket might be in-bounds for some programs and a ToS problem on others—read the policy.
OAI versus OAC versus "we thought we did"
Origin Access Identity and Origin Access Control both depend on a bucket policy that denies everything except the CloudFront service principal. Misconfigs look like:
- Policy never applied; ACL still
public-read - Policy allows
Principal: "*"fors3:GetObject"temporarily" - OAC created in console but distribution still points at a public website endpoint
- Secondary bucket used for uploads without the same deny rules
- CloudFront origin path rewrite that hides keys on the CDN URL while the raw key remains fetchable on S3
If direct access fails with AccessDenied and only CloudFront succeeds, that path is healthy—move on. If direct access returns 200 with body bytes, you have the finding.
Compare ETag or content hashes between CDN and origin responses so triage cannot claim you hit a different object. If the distribution adds security headers the origin lacks, that difference itself documents you left the CDN path.
Signed URL confusion
Sometimes CloudFront requires signed URLs while S3 does not. Fetch the object with a cold client that never saw the signature. Compare Cache-Control and body hashes. For HTML or JSON that embeds non-public data, severity jumps; for a truly public marketing asset, mark it informative and move on unless the bucket listing is also open.
ListBucket via anonymous access is a separate, often higher, issue. Mention it only if you can demonstrate it. A single private invoice PDF readable by key is already enough for a solid medium or high depending on data class.
Report content
Include both URLs, response status codes, a redacted body snippet or hash, and the inferred missing deny statement. Recommend OAC with an explicit bucket policy, Block Public Access enabled, and CloudFront as the only read path for private objects. Purge any objects that were exposed and rotate secrets embedded in those files.
When the CDN is a costume and S3 still performs for strangers, say that plainly.