← All case studies

Private isn't segmented: the PCI DSS Requirement 1 gaps I keep finding in the cloud

Cloud card data environments often look segmented on a diagram: no public IPs, a firewall in front. These are the seven PCI DSS Requirement 1 gaps I keep finding behind that, how I close them, and the guardrails that stop them coming back.

Reading time
13 min

Results at a glance

Recurring Requirement 1 gap patterns
7
Gaps closed on my latest assessment
All
Open ports without approved need
0
Sub-requirements mapped to evidence
All in scope
Reference cardholder data flow and network controls: an external payment network reaches a hub firewall, which forwards only to the payment gateway; the web server calls the payment gateway, which writes to the cardholder database. Each system sits behind its own network security group, and administrators reach the database only through a jump host outside the card data environment.

TL;DR

  • The pattern: across my PCI DSS work, cloud card data environments (CDEs) often have no public IPs and sit behind a firewall, so they’re assumed to be segmented. Tested rule by rule against PCI DSS v4.0 Requirement 1, the same seven gaps keep showing up.
  • The method: scope the CDE tightly, judge the effective access on every path (firewall, routes, and security groups at every layer), map each finding to the sub-requirement an assessor will test, fix it without interrupting payments, and evidence everything. I own it end to end. An AI agent does the first pass over configuration exports and drafts the paperwork; it never touches the environment or card data, and I make and verify every change.
  • The outcome: on my latest Requirement 1 assessment, every gap was closed and the CDE was ready for formal assessment: access enforced by the network, not by passwords or assumptions; every open port with an approved business justification; current network and data flow diagrams; and every control backed by evidence an assessor can verify.

The lesson in one line: “private” is not “segmented”. No public IP tells you nothing about what’s allowed once traffic is inside the network.

Context

PCI DSS is the global security standard for any organisation that stores, processes or transmits payment card data, which covers almost every fintech, bank and payment provider. Requirement 1, “Install and maintain network security controls”, is its foundation: if the network doesn’t isolate card data, every later control has a larger surface to protect.

The setup I usually walk into is a sound one on paper: a hub-and-spoke network, a central firewall in the hub, the CDE in a peered production network, no public IPs on in-scope systems, and administrators coming in through cloud identity and a jump host.

On a diagram, that looks finished. My job is to prove whether the rules match the diagram.

The seven gaps I keep finding

Behind a clean architecture, the real controls are usually thinner than they look. These are the patterns, and each one maps to the fix and the guardrail further down:

  1. One broad rule undoing many narrow ones. A single allow rule from any source, often on a web tier, cancels out an otherwise well-restricted configuration.
  2. A database guarded by credentials, not the network. “Only the jump host connects to the database” is true in practice, but only because of passwords. Nothing at the network layer enforces it.
  3. Just-in-time access that never applies. Time-boxed admin access is configured, but a standing allow rule with a higher priority matches first, so the time-boxing protects nothing.
  4. Shared subnets widening the scope. A CDE system shares a subnet with unrelated systems under broad rules. The CDE system may be protected, but the shared rules pull more of the estate into scope than necessary.
  5. Open ports without a recorded reason. Requirement 1.2.5 expects every allowed service, protocol and port to have a defined business need. Some ports are open and nobody can say why.
  6. Partner access scoped by source, not by service. An inbound partner rule restricts who can connect, but not which ports.
  7. No current diagrams. Requirements 1.2.3 and 1.2.4 call for an accurate network diagram and cardholder data flow diagram. They’re missing or out of date.

What makes this work hard

  • Live card processing. No change can interrupt payment traffic, so every rule is checked against real flows before it’s tightened.
  • Layered controls. Subnet- and NIC-level security groups both apply, so a rule that looks open at one layer may be closed at the other. Every finding has to be judged on the effective rules, not one layer at a time.
  • Evidence, not assumption. An assessor accepts what can be shown. Every control needs a rule, a configuration record or a diagram behind it.

How I run these assessments: human-governed, AI-assisted

Regulated teams are right to be wary of AI near card data. The answer isn’t to avoid it; it’s to decide exactly what the agent sees and does. The agent does the reading and the first drafts, and I keep the judgement and every change:

  • Configuration, never card data. The agent works only on exported network configuration: security group, firewall and route rules. It has no access to the cloud environment, to CDE hosts or to any system holding card data, and nothing it reads contains cardholder data, credentials or keys.
  • First pass over the rules. It parses the exports and flags candidates: rules open to any source, overlapping rules, and higher-priority rules that shadow narrower ones. I then confirm effective access myself on every path, because a rule that looks open in one layer can be closed in another.
  • First drafts of the paperwork. It drafts the findings write-up, the mapping to Requirement 1 sub-requirements and the CDE diagram from my description. I correct and approve every line, and check the diagram against the live environment.
  • Every change is mine. I make and verify every rule change myself, against observed traffic.
flowchart LR
  accTitle: Human-governed, AI-assisted assessment loop
  exp[Config exports<br/>no card data, no secrets] --> agent[AI agent<br/>flag rules, draft findings]
  agent --> me[My review<br/>effective access, decisions]
  me --> chg[Change, made by me<br/>checked against live traffic]
  chg --> ev[Evidence pack<br/>per sub-requirement]

That split is where the time goes: less on reading rule lists and formatting evidence, more on the decisions an assessor will test.

Approach and decisions

Decision Alternative Why
Scope the CDE to the systems that store, process or transmit card data Assess the whole production network at once A tight scope gives defensible findings quickly. Systems that connect to the CDE, like a jump host, are assessed as connections, not as CDE members.
Judge every rule on effective access (firewall + routes + subnet and NIC security groups) Review each security group in isolation Single-layer reviews produce both false alarms and false comfort.
Keep the jump host outside the CDE Pull it into scope It’s the access path in, not part of the CDE. Its traffic to the database is inbound CDE traffic under 1.3.1, which is exactly what the database’s controls should restrict.
Enforce “jump host only” at the network layer Keep relying on database credentials Credentials are an authentication control. Requirement 1 asks the network itself to restrict access.
Remove standing admin rules instead of adding more JIT Re-prioritise around them Just-in-time access only works if nothing is permanently allowed. The fix is to remove the standing rule.
Treat shared subnets as their own finding Close it because the CDE system is protected One protected system doesn’t make the subnet safe, and every rule on a shared subnet becomes a CDE decision.
One focused network and data flow diagram for the CDE A full-estate diagram A focused diagram answers 1.2.3 and 1.2.4 directly and stays accurate when the wider estate changes.

The method, step by step

A reference CDE, with each control on the boundary it protects:

flowchart LR
  accTitle: Reference cardholder data flow and network controls
  ext[External payment network<br/>untrusted] --> fw[Hub firewall<br/>scoped forwarding, default deny]
  subgraph cde[Card data environment]
    web[Web server<br/>security group] --> pgw[Payment gateway<br/>subnet + NIC security groups]
    pgw --> db[(Cardholder database<br/>private, no public endpoint)]
  end
  fw --> pgw
  admin[Administrator<br/>cloud identity] --> jump[Jump host<br/>outside the CDE]
  jump -->|database port only| db

1. Scope and inventory. List every network control on the CDE path: the firewall’s rules for each in-scope system, network peering, each subnet and NIC security group, and the route tables.

2. Map the cardholder data flow. Trace how card data actually moves, then draw one combined network and data flow diagram with each control on the boundary it protects.

3. Review every rule against Requirement 1. Tie each finding to the sub-requirement an assessor will test:

Sub-requirement What it asks The fix
1.2.3 / 1.2.4 Accurate network and cardholder data flow diagrams Built and kept current
1.2.5 Every allowed service, protocol and port identified, with a defined business need Ports without a business need closed; every port that stays open has a documented, approved business justification
1.3.1 Inbound CDE traffic restricted to what’s necessary Broad rules removed; database access limited to the jump host; partner access limited to the required service
1.3.2 Outbound CDE traffic restricted to what’s necessary Restricted, with everything else denied
1.4.1 Network security controls between trusted and untrusted networks Hub firewall forwards only to specific private addresses
1.4.2 Inbound traffic from untrusted networks limited, everything else denied Source ranges checked; default deny confirmed
1.4.4 Systems storing card data not directly reachable from untrusted networks Database private, with no public endpoint
Least-privilege admin access Standing admin rules removed, so just-in-time access is the only way in
Segmentation and routing hygiene Shared-subnet rules tightened; unneeded cross-environment routes removed

4. Remediate without interrupting payments. I implement every fix myself, confirming each change against observed traffic before tightening it: close every port without a business need and get the justification for each remaining one approved, remove the broad rules, enforce database access at the network layer, limit partner and outbound traffic to what’s required, and clean up shared-subnet rules and unneeded routes. The database pattern shows the principle: allow exactly one source on exactly one port, and deny everything else (sanitised):

az network nsg create -g <rg> -n <cde-db-nsg>

az network nsg rule create -g <rg> --nsg-name <cde-db-nsg> \
  -n Allow-JumpHost-DB --priority 100 --direction Inbound --access Allow \
  --protocol Tcp --source-address-prefixes <jump-host-ip>/32 \
  --destination-port-ranges <db-port>

az network nsg rule create -g <rg> --nsg-name <cde-db-nsg> \
  -n Deny-All-Inbound --priority 4096 --direction Inbound --access Deny \
  --protocol '*' --source-address-prefixes '*' --destination-port-ranges '*'

az network vnet subnet update -g <rg> --vnet-name <prod-vnet> \
  -n <cde-db-subnet> --network-security-group <cde-db-nsg>

For just-in-time access, the fix is subtraction: delete the standing allow rules, and the time-boxed rules become the only path in.

5. Verify and evidence. Re-check the effective rules on every CDE path after the changes, then write up each finding with its sub-requirement, evidence, risk and remediation, so the CDE can be walked through control by control in an assessment.

What “done” looks like

Measure Typical starting point After remediation
Requirement 1 gaps Open All closed
Any-source allow rules into the CDE Present None
Open ports Some with no recorded reason Every one with an approved business justification
Database access Enforced by credentials only Enforced by the network, jump host only
Just-in-time admin access Bypassed by standing rules The only way in
CDE network and data flow diagram Missing or out of date Current, every in-scope system mapped
Sub-requirements with mapped evidence None All in scope

That’s where my latest Requirement 1 assessment ended: every gap closed, each control verified and evidenced, and the CDE ready for formal assessment. Beyond the rules, it also became explainable. One diagram shows how card data moves and which control sits at each boundary, and every control is tied to the requirement an assessor will test.

Keeping it fixed: a guardrail for every gap

Closing gaps once is the easy part. Requirement 1 is lost one rule change at a time, so every gap needs a control that stops it coming back, enforced by code rather than memory. These are the guardrails I attach to each pattern, in three layers: in the pull request (CI checks on the Terraform plan), at deploy time (cloud policy) and on a schedule (drift detection):

Gap Guardrail Enforced by
One any-source rule undoing the narrow ones No inbound allow from any source in CDE scope Azure Policy deny on CDE resource groups, plus a CI check on the plan
Database guarded by credentials only The data tier ships from one module: one source, one port, explicit deny Terraform module for the CDE data tier; drift alert if a rule outside the baseline appears
Standing rules bypassing just-in-time access No permanent allow on admin ports in the CDE Policy deny on admin-port allow rules; just-in-time is the only way in
CDE systems on shared subnets CDE subnets hold CDE systems only Policy deny on attaching a network interface to a CDE subnet unless the resource is tagged in scope
Partner access scoped by source, not service Every allow rule names its ports CI fails any CDE allow rule with a wildcard port range
Open ports without a recorded reason No justification, no rule CI fails any CDE rule without a business justification and approval reference in its description
No current diagrams Diagram as code, changed in the same pull request as the rules Mermaid diagram in the repo; CDE rule changes require a review from the compliance owner

And for the six-monthly review that Requirement 1.2.7 expects: a scheduled job exports the effective rules for every CDE network interface and diffs them against the approved baseline. An AI agent triages the diff read-only and drafts the review, and a human approves it. The review becomes a diff to sign off, not a rule list to re-read.

Sketch of the deny policy (sanitised; a full version also checks rules defined inline on the security group, and scopes the assignment to CDE resource groups):

{
  "if": {
    "allOf": [
      { "field": "type", "equals": "Microsoft.Network/networkSecurityGroups/securityRules" },
      { "field": "Microsoft.Network/networkSecurityGroups/securityRules/direction", "equals": "Inbound" },
      { "field": "Microsoft.Network/networkSecurityGroups/securityRules/access", "equals": "Allow" },
      { "field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix", "in": ["*", "Internet", "0.0.0.0/0"] }
    ]
  },
  "then": { "effect": "deny" }
}

The same controls on AWS and Google Cloud

The examples here are from Azure, but the method is cloud-agnostic: scope tightly, judge effective access, justify every open port, and evidence everything. The equivalent building blocks:

Control Azure AWS Google Cloud
Per-workload rules Network security groups (subnet + NIC) Security groups + network ACLs VPC firewall rules, firewall policies
Central inspection Azure Firewall in a hub AWS Network Firewall, inspection VPC Cloud NGFW
Prove effective access Effective security rules Reachability Analyzer, Network Access Analyzer Connectivity Tests, Firewall Insights
Admin access without standing rules Defender for Cloud just-in-time access Systems Manager Session Manager Identity-Aware Proxy TCP forwarding
Block a broad rule Azure Policy (deny) AWS Config rules, service control policies Organization Policy

Lessons

  • Start from effective rules, not rule lists. The effective security rules view per network interface shortens the layered analysis. Pull it first.
  • Give the CDE its own subnets. Sharing a subnet makes every rule on it a CDE decision. Dedicated subnets shrink the scope an assessor has to test.
  • Record justifications as rules are written. Recovering the business need for an old port is slow. Capturing it in the rule’s description at creation time costs seconds.
  • Build on the foundation. Requirement 1 is what the rest of the standard stands on: with the network proven, every later control has a smaller, well-defined surface to protect.

Preparing a card data environment for PCI DSS? Whether you’re a fintech, a bank or a payment provider, on Azure, AWS or Google Cloud, this is work I do: scoping the CDE, proving segmentation rule by rule, and leaving evidence an assessor can verify.

Tech stack

PCI DSS v4.0 · Azure · Azure Firewall · Network Security Groups · Microsoft Defender for Cloud · Azure CLI