The ticket
A researcher DMs a URL: tc-lab-app-logs/backup/pay-api.sql returns 200. The ticket that opened the bucket said “need the logo on the website.” Nobody scoped the policy to one prefix. Account Block Public Access was off “because the website.”
S3 Block Public Access is a guardrail at the account and the bucket. Four settings: BlockPublicAcls, IgnorePublicAcls, BlockPublicPolicy, RestrictPublicBuckets. Account-level settings apply across buckets. ACLs are the old object/bucket grant model; bucket policies are the resource policy you should actually read. Object Ownership Bucket owner enforced disables ACLs. “Public for the logo” belongs in a dedicated public-assets design (or CloudFront), never on the logs/backup bucket. IAM Access Analyzer reports public and cross-account access — use it as evidence.
Account 111122223333 · OUs Security / Workloads · hub VPC 10.50.0.0/16 · app VPC 10.51.0.0/16 · SG sg-web · NACL acl-app · bucket tc-lab-app-logs · role AppEC2Role · TGW tgw-lab · VGW public 203.0.113.60 · on-prem FortiGate WAN 203.0.113.10. Dummy account ID. Not yours.
Mental model
| Layer | Job | This lab |
|---|---|---|
| Account Block Public Access | Hard stop on public ACLs/policies | On for 111122223333 |
| Bucket BPA | Same four switches per bucket | On for tc-lab-app-logs |
| Bucket policy | Who can s3:* on this bucket/prefix | AppEC2Role write; no Principal * |
| ACL | Legacy grants (AllUsers / AuthenticatedUsers) | Disabled via Bucket owner enforced |
| Access Analyzer | Finds public / cross-account | Must be quiet for logs |
If the logo must be public, it does not live with pay-api.sql.
Account 111122223333 + tc-lab-app-logs
How to choose
| Need | Do | Do not |
|---|---|---|
| Website logo | Dedicated public-assets path / CloudFront | Principal * on the logs bucket |
| App logs + backups | Private + BPA + CMK (lesson 5) | ACL public-read “for one file” |
| Prove it is not public | Access Analyzer + get-public-access-block | Trust the folder name |
Runbook
Side A — kill switch
Account-level Block Public Access on. Confirm bucket-level on
tc-lab-app-logs. Object Ownership: Bucket owner enforced.Side B — policy
Remove Principal * and any AllUsers ACL. Write a bucket policy for
AppEC2Roleonly. Movelogo.pngout.Side C — prove
Access Analyzer is quiet. Anonymous GET fails. CloudTrail (lesson 7) data events tell you who already read it — that is incident scope, not optional colour.
aws s3control get-public-access-block --account-id 111122223333
# BlockPublicAcls / IgnorePublicAcls / BlockPublicPolicy / RestrictPublicBuckets: true
aws s3api get-public-access-block --bucket tc-lab-app-logs
aws s3api get-bucket-policy --bucket tc-lab-app-logs
aws s3api get-bucket-ownership-controls --bucket tc-lab-app-logs
# ObjectOwnership: BucketOwnerEnforced
aws accessanalyzer list-findings --analyzer-arn arn:aws:access-analyzer:ap-south-1:111122223333:analyzer/lab \
--filter '{"resource":{"eq":["arn:aws:s3:::tc-lab-app-logs"]}}'
# empty public findingsFour failures
1 · Public for the logo
The policy was bucket-wide. The backup went with it.
2 · Account BPA off “for the website”
Now every future bucket can be born public. Keep account BPA on; isolate the one public design.
3 · ACL public-read, policy looks clean
You did not look at ACLs. Disable them.
4 · Closed the URL, skipped Analyzer + Trail
You do not know who already copied pay-api.sql. Contain, then scope.
How to prove it
1) Account and bucket BPA are on. 2) Analyzer has no public finding on tc-lab-app-logs. 3) Anonymous GET is 403. 4) You scoped CloudTrail data events if the object was live.
Traps
| Belief | Fact |
|---|---|
| AuthenticatedUsers is “our account” | It is any AWS-authenticated principal, not your org |
| Folder “public/” is a security boundary | Only the policy is |
| BPA deletes the bad ACL | It blocks the effect; still clean the ACL/policy |
Knowledge check
Judgment items. One best answer. Reasons send you back to the matching section.
AWS security class series: Shared + IAM · Org / OU · SG vs NACL · Role not key · KMS + Secrets · VPC endpoint · Trail + GuardDuty · TGW hub · S3 public · Interview
Sources
Related: AWS session factory · Migration series.