PCCSE study materials
PCCSE study resources

A public-S3 policy that reports wildcard principals instead of real exposure

PCCSE · Put your knowledge to work in context.

All case studies

A public-S3 policy that reports wildcard principals instead of real exposure

Six months ago your team wrote a custom Prisma Cloud config policy to flag publicly readable S3 buckets. It now generates about 3,400 alerts per week across 900 buckets in 42 accounts, and the SOC has started ignoring the S3 alert channel. Sampling shows three populations: roughly 60% of buckets have account-level S3 Block Public Access enabled but still carry a bucket policy containing a Principal: "*" statement; about 15% are static-website buckets in the marketing account that the CISO has formally accepted with a WAF and a restrictive bucket policy as compensating controls; the remainder are genuinely exposed. Separately, the built-in S3 encryption policy alerts on 700 buckets because the organisation standardised on SSE-KMS with bucket keys, while the policy logic was written against SSE-S3. The CISO wants fewer alerts but is explicit that no genuinely public bucket may go unreported.

1. How do you rewrite the policy logic so it measures *effective* public exposure rather than the presence of a wildcard principal? Show the reasoning, and how you would validate the change before it reaches production.

Read the worked answer

Start from the definition, not the syntax. The business question is "can an unauthenticated internet user read an object in this bucket?" A wildcard principal is necessary but not sufficient. In S3, effective public access depends on the interaction of: the bucket policy statement (principal, action, resource, and any aws:SourceVpce/IP conditions), the bucket ACL, the four bucket-level Block Public Access settings, and the account-level Block Public Access settings applied by every account that owns the bucket. RestrictPublicBuckets and IgnorePublicAcls can neutralise a policy that looks terrifying.

Policy design. Build the RQL so the exposed set is expressed as an intersection, not a single match: bucket policy permits s3:GetObject to * and not blocked by the relevant public access block settings. In practice you combine the bucket-config resource (policy, ACL) with public-access-block data via boolean operators in json.rule; if the account-level block is not present in the collected configuration, no RQL will see it - so the first work item may be confirming that the relevant describe/get APIs are collected. Where a clean single-resource join is impossible, split into two policies: bucket-level exposure, and an account-level policy that verifies Block Public Access is on everywhere (which is the cheaper, more durable control anyway).

Validation before rollout. Do not publish and hope:

  1. Run the new RQL as an ad-hoc query in Investigate against all 42 accounts and export the result set.
  2. Diff it against the current 900-bucket set. Expected differences: the ~60% blocked group disappears, the 15% accepted group either disappears or is isolated, and the genuinely exposed group is unchanged. Any genuine exposure that disappears is a bug in your logic.
  3. Run the diff a second time a week later to catch newly created buckets.
  4. Only then create the policy, and keep the old policy running in parallel for one scan cycle so you can prove the delta to the CISO.

2. How do you handle the 15% accepted-risk marketing buckets in a way that stays auditable and does not create a blind spot? Compare the mechanisms you would consider.

Read the worked answer

The trap to avoid: scoping the policy out of the marketing account group. That silences today's 135 buckets and also every bucket the marketing team creates next quarter - a permanent blind spot created to solve a temporary problem. It also makes the risk acceptance invisible to the next CISO.

Mechanisms, in order of preference:

  1. Resource-level exception with an owner, ticket, and expiry date. The finding stays associated with the bucket, the exception records who accepted it and why, and it expires so it must be re-justified. This is the only option that survives staff turnover. Verify what your tenant supports for exceptions/allowlisting and keep an external register as the source of truth if the product's record is thin.
  2. Keep detecting, change routing. Tag the accepted buckets and write an alert rule that sends them to a low-priority queue or weekly digest instead of the SOC queue. Nothing is suppressed; the signal just stops competing with real incidents. This is usually the right complement to option 1.
  3. Compensating-control verification. Add a policy that asserts the compensating controls still exist - bucket policy restricted, WAF still attached, no s3:GetObject to * without condition. If the WAF is detached, the acceptance is void and the alert should escalate automatically. Risk acceptances rot precisely because nobody re-tests the control.
  4. Disable the policy for the account group. Reject; see above.

Recommendation: resource-level, time-boxed exceptions plus tag-based routing plus a compensating-control policy, with a quarterly review where the CISO re-signs. Report the accepted count alongside the open count so the number is never quietly forgotten.

3. The encryption policy's 700 false positives: what is your fix, and how do you decide the severity of the resulting policy? What is the general lesson for rolling out policy changes?

Read the worked answer

Diagnosis first. SSE-KMS with bucket keys is not a weaker control than SSE-S3 - it is stronger, with a different cost and key-management model. The finding is a logic defect in the policy, not a real risk, and the fix is to make the RQL accept the current organisational standard (SSE-KMS, including the bucket-key variant) while still flagging buckets with no default encryption at all. If you want to keep a meaningful KMS check, alert on the key policy permitting broad kms:Decrypt to * rather than on the presence of KMS itself.

Severity decision. Severity should reflect impact if exploited, not how annoying the finding is. "No default encryption configured" is a genuine control gap but rarely an internet-facing exposure, so it belongs below the public-exposure policies - informational/low for standard buckets, medium where regulated data tags apply. The mistake to avoid is inflating severity to get attention; that is what trained the SOC to ignore the channel in the first place.

A better architecture. Enforce default encryption at the account/organisation level with an SCP and a single account-level policy, then keep a small per-bucket policy for exceptions. One account-level check replaces 700 per-resource alerts and matches how the control is actually implemented.

General rollout lesson. A policy change is a production change to a detection system. Back-test the new logic against real data, diff old versus new result sets, announce the change with expected volume, watch the first 48 hours for a spike or a collapse, and record the before/after counts. The failure mode is silent: a policy that stops matching looks identical to a clean environment.