Bucket-Level Public Access Block Silently Loses to a Permissive Bucket ACL
Enabling S3 Public Access Block at the bucket level does not remove a pre-existing permissive bucket ACL; the ACL grant can remain active until it is directly corrected and account-level Block Public Access is confirmed.
Operational summary
At a glance
- Symptom
- A team enables all four Public Access Block settings directly on an S3 bucket and expects the bucket to be fully private, then finds…
- Likely cause
- S3 access evaluation combines several independent layers: account-level Block Public Access, bucket-level Block Public Access, bucket policy, bucket ACL and object ACL.
- Impact
- Objects intended to be private remain reachable by anonymous or authenticated AWS users through the pre-existing ACL grant, even though the bucket-level Public Access Block indicator shows all…
- Verification signal
- Validation must independently confirm object-level reachability rather than trusting console indicators alone.Query the bucket ACL again after correction and confirm no grant remains for AllUsers or AuthenticatedUsers.Confirm account-level…
- Safe correction
- The correction addresses the ACL grant directly rather than relying on Public Access Block alone to neutralise it.
- Rollback or recovery
- If removing the ACL grant or enabling account-level Block Public Access breaks a legitimate access pattern, such as a static website or cross-account integration, reverse the change in…
Symptom
A team enables all four Public Access Block settings directly on an S3 bucket and expects the bucket to be fully private, then finds during a review that a legacy bucket ACL grant to the AllUsers or AuthenticatedUsers group still permits reads through certain access paths, and that account-level Block Public Access was never confirmed as enabled.
The bucket console shows Public Access Block as “On” for all four settings at the bucket level. Engineers reasonably conclude the bucket is closed to public access. Bucket ACL history is not reviewed because the team assumes Public Access Block supersedes ACLs entirely going forward.
False Assumption
The team assumed that enabling bucket-level Public Access Block settings is equivalent to removing public grants, and that it applies uniformly regardless of account-level configuration or pre-existing ACL grants that predate the setting change.
In fact, Public Access Block operates as independent boolean settings (BlockPublicAcls, IgnorePublicAcls, BlockPublicPolicy, RestrictPublicBuckets) that must each be true at the relevant scope for the intended effect, and bucket-level settings do not automatically substitute for account-level settings. If account-level Block Public Access was never enabled, and only bucket-level settings were configured, a legacy ACL grant remains active and enforceable for existing objects.
Root Cause
S3 access evaluation combines several independent layers: account-level Block Public Access, bucket-level Block Public Access, bucket policy, bucket ACL and object ACL. Each layer is evaluated on its own terms. BlockPublicAcls only prevents new public ACLs from being applied going forward; it does not retroactively strip existing public grants already present on the bucket. IgnorePublicAcls is the specific setting that causes S3 to disregard existing public ACL grants during authorisation decisions. If IgnorePublicAcls is false at the effective scope, any pre-existing public ACL grant continues to be honoured regardless of how the bucket-level indicator reads.
This is documented AWS behaviour: Block Public Access settings are additive controls layered over existing permissions models, not a single override switch. The Well-Architected Reliability Pillar describes operational resilience practices consistent with treating access-control changes as verifiable, evidence-based operations rather than assumed-correct toggles: a control that appears “on” in a console still requires confirmation that its effective scope actually removes the exposure it is meant to close.
Impact
Objects intended to be private remain reachable by anonymous or authenticated AWS users through the pre-existing ACL grant, even though the bucket-level Public Access Block indicator shows all settings enabled. The exposure is inconsistent and easy to miss: some access paths evaluated against bucket policy may correctly deny access, while ACL-based access paths continue to permit it, producing confusing partial results during a manual spot check.
The operational consequence is a false sense of closure: a review that checks only the bucket-level Public Access Block indicators will report the bucket as protected, while the actual object exposure persists until the underlying ACL grant is removed and account-level Block Public Access is confirmed.
Diagnosis
Confirm the actual effective access state rather than relying on the bucket-level indicator alone.
- Retrieve the bucket-level Public Access Block configuration and confirm all four settings are true.
- Retrieve the account-level Public Access Block configuration separately; a bucket-level “On” state does not confirm the account-level state.
- Retrieve the bucket ACL directly and inspect grants for the AllUsers or AuthenticatedUsers predefined groups.
- Retrieve the bucket policy and confirm whether it independently permits or denies public access, since policy and ACL are evaluated as separate layers.
- Cross-reference S3 Access Analyzer or Storage Lens findings, if enabled, for buckets reported public despite Block Public Access appearing enabled.
Correction
The correction addresses the ACL grant directly rather than relying on Public Access Block alone to neutralise it. Every change below must be applied and verified by a human operator with confirmed permissions in an isolated or non-production environment before any production change.
- Confirm the current bucket ACL contents and identify the exact grantee URI responsible for the public grant.
- Enable account-level Block Public Access if it is not already enabled, so IgnorePublicAcls takes effect uniformly across the account rather than only at the bucket the team happened to check.
- Replace the bucket ACL with a private ACL that removes the public grantee, rather than depending solely on IgnorePublicAcls to mask it; masking leaves the grant present and re-exposable if Public Access Block is later disabled elsewhere.
- Where feasible, move to the Bucket owner enforced object ownership setting, which disables ACLs entirely for new uploads and removes this class of trap for future objects, subject to confirming application compatibility with any existing ACL-based access pattern first.
Validation
Validation must independently confirm object-level reachability rather than trusting console indicators alone.
- Query the bucket ACL again after correction and confirm no grant remains for AllUsers or AuthenticatedUsers.
- Confirm account-level Block Public Access shows all four settings enabled.
- Attempt an unauthenticated read of a designated, non-sensitive test object created specifically for this check, and confirm it is denied.
- Confirm S3 Access Analyzer or an equivalent finding no longer reports the bucket as publicly accessible, allowing time for the finding to refresh.
Rollback
If removing the ACL grant or enabling account-level Block Public Access breaks a legitimate access pattern, such as a static website or cross-account integration, reverse the change in a controlled, verified sequence rather than restoring the original public grant blindly.
- Identify the specific broken access pattern and confirm with the consuming team whether public ACL access was the intended mechanism or an unreviewed legacy artefact.
- If a legitimate need exists, replace the removed grant with a scoped bucket policy limited to specific principals or conditions, rather than reinstating the broad AllUsers or AuthenticatedUsers grant.
- If temporary restoration of prior behaviour is required while the scoped policy is designed, restore only the previously recorded ACL grant using the ACL JSON captured before the change, and set an explicit stop condition: this restoration is temporary and must be replaced with a scoped policy within an agreed short window, owned by a named individual.
- Record the rollback action, the reason, and the follow-up owner so the temporary reopening is not forgotten and is tracked to closure.
Prevention
Treat Public Access Block as one layer among several, not a single switch that supersedes ACLs and policy. Enable account-level Block Public Access as the baseline control for the account rather than relying on per-bucket settings alone. Adopt Bucket owner enforced object ownership on new buckets so ACLs are disabled by default, removing this failure path for future resources. Include an explicit ACL and bucket policy review, not just a Public Access Block indicator check, in any access control audit or change validation for S3 resources handling material data.
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.
The correction addresses the ACL grant directly rather than relying on Public Access Block alone to neutralise it.
Validate the vendor-specific syntax in official documentation before applying it.
Verify, roll back or escalate
Verify
Validation must independently confirm object-level reachability rather than trusting console indicators alone.Query the bucket ACL again after correction and confirm no grant remains for AllUsers or AuthenticatedUsers.Confirm account-level Block Public Access shows all four settings enabled.Attempt an unauthenticated read of…
Rollback
If removing the ACL grant or enabling account-level Block Public Access breaks a legitimate access pattern, such as a static website or cross-account integration, reverse the change in a controlled, verified sequence rather than restoring the original public grant blindly.Identify…
Escalate
Escalate when the blast radius is uncertain, the control cannot be tested safely, or remediation requires an outage or security exception.