S3 Block Public Access + SCP: Two Layers That Make Accidental Public Buckets Impossible

Ned
Ned Cloud Engineer
· Updated · 3 min read
S3 Block Public Access + SCP: Two Layers That Make Accidental Public Buckets Impossible

The Problem

Your image hosting company stores objects in S3. You want to prevent accidental public exposure of any object in the entire account. No bucket, no matter who creates it, should ever be public.

The Solution

Two preventive controls stacked:

  1. S3 Block Public Access at the account level — prevents any bucket from becoming public
  2. Organizations SCP that prevents IAM users (including root) from disabling that setting

Together they make public exposure structurally impossible, not just detectable.

How It Works

Step 1: Enable Account-Level Block Public Access

1
2
3
4
aws s3control put-public-access-block \
  --account-id 123456789012 \
  --public-access-block-configuration \
    "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"

The four settings do different things:

Setting What It Blocks
BlockPublicAcls Rejects any request to set a public ACL
IgnorePublicAcls Existing public ACLs are ignored
BlockPublicPolicy Rejects any bucket policy that allows public access
RestrictPublicBuckets Existing public buckets become inaccessible to unsigned callers

All four true = maximum lockdown.

Step 2: Wrap It in an SCP

Detective controls only work after the fact. An SCP is preventive — it stops the API call before it happens.

1
2
3
4
5
6
7
8
9
10
11
12
{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "PreventBpaChanges",
    "Effect": "Deny",
    "Action": [
      "s3:PutAccountPublicAccessBlock",
      "s3:PutBucketPublicAccessBlock"
    ],
    "Resource": "*"
  }]
}

Attach this SCP to the account (or root OU). Now no principal in the account can turn off Block Public Access — not admins, not IAM users, not even root.

Why Preventive Beats Detective

Detective controls (like GuardDuty finding a public bucket or Trusted Advisor alerting) have a time-to-detection gap. During that window, the bucket is publicly exposed. Preventive controls close that window entirely.

1
2
Detective:  Bucket goes public → Alert fires → Human/Lambda fixes → [Window of exposure]
Preventive: Attempt to make public → API call denied → No window

Why Not the Alternatives?

GuardDuty + Lambda remediation — Detective and reactive. There is always a window where the bucket is public. Also depends on GuardDuty being enabled and Lambda succeeding.

Trusted Advisor with email alerts — Slow detection cycle, manual remediation. Weakest option.

Resource Access Manager to find public buckets — RAM is for sharing resources across accounts, not for detecting public buckets. Completely unrelated to this problem.

Key Takeaways

  • Preventive controls stop the bad action; detective controls notice after the fact
  • Enable all four Block Public Access settings — they cover different attack surfaces
  • Wrap Block Public Access in an SCP so even root cannot disable it
  • SCPs apply to every principal in the account, including the root user
  • Combine account-level BPA with bucket-level BPA for defense in depth on shared accounts
Rating:
Share
Previous Client-Side Encryption: The Only Way to Encrypt S3 Data Before It Leaves Your Machine Next aws:PrincipalOrgID: One Bucket Policy That Grants Access to Every Account in Your Organization