Misconfiguration detection
The public bucket and the open Postgres port are the two breaches that keep happening. Detection finds them after the fact; preventive guardrails make the misconfig structurally impossible. Seniors build both, and lead with prevention.
Two breaches account for an outsized share of cloud incidents, and they are almost comically boring. The first: a storage bucket holding customer exports gets its ACL flipped to public — sometimes by an engineer sharing a demo asset, sometimes by a Terraform default nobody read — and a researcher (or a crawler) finds it through a wordlist of bucket names in an afternoon. The second: a security group ingress opens 0.0.0.0/0 on port 5432, and a managed Postgres that should only ever talk to the app tier is now answering the entire internet, where credential-stuffing bots will find it within hours. No zero-day, no malware, no clever chain — just config that says “yes” when it should say “no.” Detection tells you it happened. The senior move is to make it unable to happen.
By the end of this lesson you’ll know how to detect a public bucket or open service at scale, why detection alone always loses the race, and which preventive guardrails turn the misconfig from a finding into an impossibility.
The two exposures that actually breach you
A misconfiguration is a resource whose effective state grants access it shouldn’t — and “effective” is the load-bearing word. The public-bucket case is rarely a single toggle: an S3 bucket’s reachability is the resolved sum of its ACL, its bucket policy, the account-level Block Public Access setting, and any access-point policy layered on top. An engineer can leave the bucket policy locked down and still expose every object by flipping one object ACL, because the most-permissive grant wins unless an explicit deny or a Block-Public-Access guardrail overrides it. That is why “we checked the policy, it’s fine” is not the same as “the bucket is private.”
The open-service case is the same shape in a different resource. A managed database, a Redis cache, an internal admin panel, an Elasticsearch node — each is supposed to live behind a private subnet or a tight allowlist. The misconfig is a security-group ingress (or NSG rule, or firewall rule) with source 0.0.0.0/0 reaching a sensitive port: 5432, 3306, 6379, 9200, 27017. The damage is not theoretical: an internet-reachable database is found by mass scanners — Shodan, Censys, and a fleet of credential-stuffing bots — typically within hours of going live, not weeks. Exposure window, not exploit cleverness, is what determines whether you get breached.
How detection actually works
Detection is mechanically a sweep: assume a read-only role in each account, call the describe/list APIs (GetBucketPolicyStatus, GetPublicAccessBlock, DescribeSecurityGroups, DescribeNetworkAcls), resolve each resource’s effective exposure, and emit a finding when it crosses a rule. The hard part is not the API calls — it is computing effective access correctly. A naive rule that only reads the bucket policy misses the object-ACL path; a naive security-group rule that flags 0.0.0.0/0 ingress misses that the instance might sit in a private subnet with no route to the internet gateway, making the open rule harmless — a false positive that, repeated a thousand times, trains your team to ignore the tool.
This is why a senior treats detection latency as the core metric. A CSPM tool that sweeps every 24 hours leaves a 24-hour window in which a public bucket is fully exposed and you don’t know. Event-driven detection — wiring config-change events (CloudTrail, AWS Config rules, EventBridge) to evaluate a resource the moment it changes — collapses that window from hours to seconds. But even seconds is a window, and a window is a chance. Which leads to the real lesson.
Prevention beats detection
Detection answers “did someone make this mistake?” after the fact. A preventive control — a guardrail — answers “this mistake cannot be made” before the fact. The canonical example is account-level S3 Block Public Access: with it enabled at the account, no bucket policy, ACL, or object grant can make any bucket public, full stop. The API call that would create the public grant is rejected. There is no exposure window because “public” was never a reachable state. Compare that to a detection rule that finds public buckets and a runbook that closes them: the detection approach has a window, depends on someone reading the alert, and re-fires every time a new engineer makes the same mistake.
The mechanism that makes prevention scale across an org is a policy guardrail enforced above the account: AWS Service Control Policies (SCPs) at the Organization level, Azure Policy with deny effect, or GCP Organization Policy constraints. These evaluate before a resource is created or mutated and deny the action outright — s3:PutBucketPublicAccessBlock that would disable the block, an ingress rule with 0.0.0.0/0 on a database port, a public-IP assignment on a database instance. A guardrail at the org level cannot be bypassed by an engineer with admin in a single account, which is precisely the property detection lacks.
| Layer | When it acts | Public-bucket control | Open-service control | Window? |
|---|---|---|---|---|
| Preventive guardrail (org) | Before the change applies | SCP denies disabling Block Public Access | SCP denies 0.0.0.0/0 on DB ports | None |
| IaC / pipeline gate | At plan / pre-apply | Policy-as-code (OPA, Checkov) fails the plan | Plan scan rejects open ingress | Console changes slip past |
| Event-driven detection | Seconds after the change | Config rule fires on ACL change | Rule fires on SG mutation | Seconds |
| Periodic CSPM sweep | Hours / daily | Effective-access sweep finds it | Cross-account scan finds it | Hours to a day |
▸Why this works
Why not just enable the org guardrail everywhere and skip detection entirely? Because preventive controls are coarse and legitimate exceptions exist — a public static-website bucket, a CDN origin, a deliberately internet-facing service behind a WAF. A blanket SCP that denies all public buckets breaks those, so teams carve exceptions, and exceptions are where drift hides. Detection is the safety net that catches what slipped through an exception path or a layer you forgot to guard. The senior posture is defense in depth: prevention as the primary control, detection as the backstop that assumes prevention has a hole — never one instead of the other.
How a senior sequences the controls
Faced with “we keep getting public buckets,” the junior reflex is to buy a scanner and write a runbook. The senior reflex is to ask why the state is reachable at all, and to push the control as far left and as high up as it will go: an org-level guardrail that denies the dangerous action, then an IaC gate so the mistake fails in review with a code owner attached, then event-driven detection for the console paths the gate can’t see, then a periodic sweep as the final backstop for whatever the upper layers missed. Each lower layer exists only to catch the failure modes of the layer above it. Detection is never the answer on its own — it is the admission that prevention is imperfect, deployed knowingly as a net, not as the floor.
A bucket holding customer PII keeps getting flipped to public by engineers sharing demo assets. The data is never meant to be public. Pick the control that actually closes the hole.
Why is computing 'effective access' the hard part of detecting a public bucket, rather than just reading the bucket policy?
An internet-reachable Postgres on `0.0.0.0/0:5432` was created via the console at 14:00. Your CSPM sweeps once daily at 02:00. What's the real risk, and the durable fix?
Order the misconfiguration controls from the one that acts earliest (highest leverage) to the one that acts last (final backstop):
- 1 Org-level preventive guardrail (SCP / Org Policy) — denies the dangerous action before it applies
- 2 IaC / pipeline gate (policy-as-code) — fails the plan in review, with a code owner
- 3 Event-driven detection — fires seconds after a console/SDK change the gate can't see
- 4 Periodic CSPM sweep — daily cross-account scan as the final backstop
- 01Walk through how you'd detect a public S3 bucket at scale, and explain why 'effective access' makes naive detection unreliable.
- 02Why does a senior lead with preventive guardrails instead of detection for the public-bucket and open-service problem, and what does defense in depth look like here?
The two cloud breaches that keep happening are boring on purpose: a storage bucket flipped public, and a database or admin service exposed on 0.0.0.0/0. Both are won by exposure window, not exploit skill — an open database port is found by mass scanners within hours, and a public bucket by a wordlist crawler in an afternoon. Detecting them at scale is a read-only sweep that must resolve effective access (ACL plus policy plus Block Public Access plus access-point policy), because a single-layer check produces false negatives that hide live exposure. But detection is always periodic and therefore late, racing the attacker across an exposure window. The senior move is to make the bad state unreachable: lead with org-level preventive guardrails — Block Public Access, SCPs that deny 0.0.0.0/0 on database ports — that reject the change before it applies and can’t be bypassed by an account admin, then layer IaC gates, event-driven detection, and a periodic sweep as backstops. Prevention is the floor; detection is the net you deploy knowing the floor has gaps. So when someone says “we keep getting public buckets,” your first question isn’t “which scanner” — it’s “why is public still a reachable state, and which guardrail makes it unreachable?”
Practice
Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.
Something unclear?
Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.