T Techclick ← All lessons
Amazon · AWS security · Lesson 9 of 10

S3 public: the logo bucket is how the backup goes public

Marketing wanted the logo public. Someone put Principal: "*" on tc-lab-app-logs because “it is the same account’s S3.” Backups and logs sat next to logo.png. Block Public Access at the account. Prefer a bucket policy over ACLs. Run IAM Access Analyzer. Public for the logo is not public for the backup.

Updated 2026-08-18·18 min read·L2 primary·Quiz at end

After this page you can

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.”

Quick interview answer

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.

Hero · one checkbox, whole bucket
Public logo object sitting in the same S3 bucket as backups
The internet does not honour your prefix intention. The policy does, or it does not.
Lab data · dummy only

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

LayerJobThis lab
Account Block Public AccessHard stop on public ACLs/policiesOn for 111122223333
Bucket BPASame four switches per bucketOn for tc-lab-app-logs
Bucket policyWho can s3:* on this bucket/prefixAppEC2Role write; no Principal *
ACLLegacy grants (AllUsers / AuthenticatedUsers)Disabled via Bucket owner enforced
Access AnalyzerFinds public / cross-accountMust be quiet for logs
Split the logo from the backup
public assetslogo only · intentional tc-lab-app-logsprivate · BPA on Principal * on logsbackup is now public

If the logo must be public, it does not live with pay-api.sql.

https://111122223333.signin.aws.amazon.com/console
Training mock · not live
VPCIAMS3CloudTrail
S3 → Block Public Access / Access Analyzer

Account 111122223333 + tc-lab-app-logs

All four ON
tc-lab-app-logs · BPA ON · Object Ownership: Bucket owner enforced
Deny public · allow AppEC2Role on prefix · no Principal *
Disabled — do not grant AllUsers
No public finding on tc-lab-app-logs
Public for the logo is not public for the backup. Training mock.

How to choose

NeedDoDo not
Website logoDedicated public-assets path / CloudFrontPrincipal * on the logs bucket
App logs + backupsPrivate + BPA + CMK (lesson 5)ACL public-read “for one file”
Prove it is not publicAccess Analyzer + get-public-access-blockTrust the folder name

Runbook

  1. Side A — kill switch

    Account-level Block Public Access on. Confirm bucket-level on tc-lab-app-logs. Object Ownership: Bucket owner enforced.

  2. Side B — policy

    Remove Principal * and any AllUsers ACL. Write a bucket policy for AppEC2Role only. Move logo.png out.

  3. 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 cli · public proof
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 findings

Four 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

Close only when

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

BeliefFact
AuthenticatedUsers is “our account”It is any AWS-authenticated principal, not your org
Folder “public/” is a security boundaryOnly the policy is
BPA deletes the bad ACLIt blocks the effect; still clean the ACL/policy

Knowledge check

Judgment items. One best answer. Reasons send you back to the matching section.

Q1

Strongest single switch so new buckets cannot be born public?

Correct: a. Account BPA.
Q2

“Public for the logo” on tc-lab-app-logs is dangerous because…

Correct: b. Same bucket.
Q3

Prefer which access language on a modern bucket?

Correct: b. Policy, not ACL.
Q4

Tool that reports a public or cross-account bucket?

Correct: a. Analyzer.
Q5

AuthenticatedUsers means…

Correct: b. Classic ACL trap.
Q6

After you flip BPA, you still need to…

Correct: a. Contain then scope.

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.