# GCP Cloud Storage Bucket Does Not Enforce Public Access Prevention

> ZopNight flags any GCS bucket whose iamConfiguration.publicAccessPrevention is not enforced, rating it critical (RC-138). The signal proves prevention is off, not that an allUsers grant exists, since no producer reads the bucket's IAM bindings. The fix is enforcing PAP first, then auditing bindings for public grants.

Source: https://zop.dev/integrations/gcp/recommendations/gcp-cloud-storage-bucket-does-not-enforce-public-access-prevention
Updated: 2026-08-19

---

## What the evidence actually proves

Public Access Prevention is the bucket setting that makes `allUsers` and
`allAuthenticatedUsers` grants impossible. ZopNight's discoverer reads
`iamConfiguration.publicAccessPrevention` during asset inventory and records whether it is
`enforced`. The rule fires when it is not, and that is precisely all it proves. Nobody has
inspected the bucket's IAM policy, so the finding does not say "this bucket is public". A
bucket with PAP off and a clean policy leaks nothing today; what it lacks is the guardrail
that stops one mistaken binding from publishing the contents tomorrow.

An earlier version of this rule claimed a confirmed `allUsers` binding. The claim was
stronger than the evidence, so the recommendation was rewritten to match what the discoverer
can prove: prevention is not enforced. The distinction matters when you triage: the finding
is a missing safety interlock, rated critical because public GCS buckets remain one of the
most common causes of cloud data breaches, not an alarm that exposure has already occurred.

## Enforce prevention, then look for grants

Fixing individual bindings without turning on prevention leaves the door unlocked. The
effective order is the reverse:

1. Enforce PAP on the bucket (existing public grants become inert immediately).
2. Then audit and delete any `allUsers` / `allAuthenticatedUsers` members from the policy.
3. Project-wide, set the org policy `constraints/storage.publicAccessPrevention` so new
   buckets inherit `enforced` and the class of finding stops recurring.

If the bucket genuinely serves a public website, mark the exception deliberately. Do not
leave PAP as `inherited` by accident.

## Verify a bucket from your terminal

```bash
gcloud storage buckets describe gs://BUCKET \
  --format="value(iamConfiguration.publicAccessPrevention)"
```

`enforced` is compliant. `inherited` means the bucket relies on whatever the org policy says,
which, absent that policy, is nothing.

## When the rule stays silent

Detection fires only on the discoverer's explicit `public_access_enabled` flag being true. A
bucket that was never enriched, with the metadata key absent entirely, is skipped rather than
flagged, so the rule abstains on incomplete data instead of guessing. Discovery itself needs
only read access: bulk inventory under `roles/cloudasset.viewer`, with per-bucket enrichment
via `storage.buckets.get`. There is no automated remediation; the change is one console
toggle or a single `gcloud storage buckets update --public-access-prevention` away, and both
are auditable in the project's activity log.
