PCI DSS v4 Engineering Controls

Implement PCI DSS version 4.0 technical requirements for payment card processing. Covers network segmentation, encryption at rest and in transit, key management, vulnerability management, and the engineering patterns that reduce cardholder data exposure.

PCI DSS v4.0 introduces a framework for engineering controls that enforce security and compliance across systems handling cardholder data. These controls are not just checklists; they are executable, verifiable, and continuously validated through engineering practices. They matter when systems must meet auditable standards for payment card processing, particularly in environments where changes occur rapidly and compliance cannot be a manual, periodic activity.

Designing the Foundation: Control Mapping and Baseline Implementation

Every PCI DSS v4.0 engineering control begins with a clear mapping to the standard’s requirements. The control matrix is not a document but an executable artifact. Each requirement is tied to a set of technical controls that are implemented via code, configuration, and automation.

The core of the implementation is a controls.json file in the root of the source repository, which defines the active controls and their status:

{
  "controls": [
    {
      "id": "1.1.1",
      "name": "All systems must be hardened according to baseline",
      "status": "active",
      "implementation": {
        "method": "ansible",
        "playbook": "roles/security/hardening/tasks/main.yml",
        "target": "all-inventory",
        "last-run": "2024-03-15T14:23:11Z",
        "result": "success",
        "output": "/var/log/ansible/hardening-2024-03-15.log"
      },
      "verification": {
        "type": "automated",
        "check": "ansible-playbook --check --diff",
        "interval": "daily",
        "schedule": "0 3 * * *",
        "report": "https://ci.example.com/verify/1.1.1"
      }
    },
    {
      "id": "3.2.4",
      "name": "Encryption of data at rest must use FIPS 140-2 validated modules",
      "status": "active",
      "implementation": {
        "method": "terraform",
        "module": "modules/encryption",
        "configuration": {
          "kms_provider": "aws-kms",
          "key_spec": "SYMMETRIC_DEFAULT",
          "validation": "fips140-2"
        },
        "enforcement": "aws-ec2-encryption"
      }
    }
  ]
}

This file is versioned in Git and used by compliance automation pipelines to verify that each control is active and validated.

Implementing Controls with Automation

Each engineering control is implemented via a pipeline that runs on every code commit or at fixed intervals. The pipeline is defined in a CI/CD configuration file, such as .github/workflows/compliance.yml:

name: PCI DSS v4 Compliance

on:
  push:
    branches: [ main ]
  schedule:
    - cron: '0 2 * * 1'  # Weekly on Monday

jobs:
  verify-controls:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install compliance tools
        run: |
          sudo apt-get update
          sudo apt-get install -y jq python3-pip
          pip3 install -r requirements.txt
      - name: Run control verification
        run: |
          python3 verify-controls.py --config controls.json --output report.json
      - name: Upload results
        run: |
          echo '{"status": "pass", "report": "report.json"}' > status.json
          echo "report.json" >> $GITHUB_OUTPUT
      - name: Update compliance dashboard
        run: |
          curl -X POST https://dash.example.com/api/compliance/updates \
            -H "Authorization: Bearer $DASHBOARD_TOKEN" \
            -d @report.json

The verify-controls.py script reads controls.json, executes the verification command for each control, and returns a report with pass/fail status and evidence.

Validating Control Execution

Validation is not a one-time check but a continuous process. Controls are validated using a standardized schema for output. Each validation run produces a report.json file:

{
  "control_id": "1.1.1",
  "timestamp": "2024-03-15T14:23:11Z",
  "status": "pass",
  "evidence": [
    {
      "type": "log",
      "source": "/var/log/ansible/hardening-2024-03-15.log",
      "content": [
        "PLAY [Apply security hardening] *******************************************************************",
        "TASK [Ensure SSH is configured] ******************************************************************",
        "changed: [web01] => {\"changed\": true, \"msg\": \"SSH configuration applied\"}",
        "TASK [Ensure firewall rules are in place] ********************************************************",
        "changed: [web01] => {\"changed\": true, \"msg\": \"Firewall rules updated\"}"
      ]
    },
    {
      "type": "output",
      "command": "ansible-playbook --check --diff",
      "stdout": "PLAY [Apply security hardening] *******************************************************************\n  \nTASK [Ensure SSH is configured] ******************************************************************\nok: [web01]\n\nTASK [Ensure firewall rules are in place] ********************************************************\nok: [web01]\n\nPLAY RECAP ****************************************************************************************\nweb01                    : ok=2   changed=0   unreachable=0   failed=0   skipped=0   rescued=0   ignored=0",
      "stderr": "",
      "exit_code": 0
    },
    {
      "type": "inventory",
      "name": "all-inventory",
      "host_count": 12,
      "hosts": [
        "web01.example.com",
        "web02.example.com",
        "db01.example.com"
      ]
    }
  ],
  "metrics": {
    "duration_seconds": 142,
    "total_tasks": 24,
    "changed_tasks": 8,
    "failed_tasks": 0
  }
}

A control is considered passing if the status field is "pass" and no evidence entries have status: "failed". A control fails if any task in the verification output reports exit_code: 1 or status: "failed".

Enforcing Control Requirements in Code

To ensure that every system adheres to PCI DSS v4.0 requirements, engineering teams use pre-commit hooks to enforce control-specific checks. The pre-commit-config.yaml file defines rules that run before every Git commit:

repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.5.0
    hooks:
      - id: check-yaml
      - id: end-of-file-fixer
      - id: trailing-whitespace
  - repo: https://github.com/PyCQA/flake8
    rev: 6.1.0
    hooks:
      - id: flake8
        args: [--max-line-length=88]
  - repo: https://github.com/PyCQA/isort
    rev: 5.13.0
    hooks:
      - id: isort
  - repo: https://github.com/ansible/ansible-lint
    rev: 6.11.0
    hooks:
      - id: ansible-lint
        args: [--profile, standard]
  - repo: https://github.com/bokeh/bokeh
    rev: 2.4.3
    hooks:
      - id: check-pci-dss
        name: "PCI DSS v4 Control Validation"
        description: "Verify that all control requirements are met in the current code state."
        entry: "pci-validate"
        language: python
        types: [python]
        pass_filenames: false
        files: "controls.json"
        args: ["--fail-on-uncertain"]

The pci-validate command checks that all control requirements in controls.json are satisfied in the current codebase. It reads each control’s verification.check field, runs it, and compares the output against expected values.

When a control requires specific configuration, pci-validate examines the actual configuration files. For example, for control 3.2.4, it checks that all EC2 instances have encryption_enabled: true and use an AWS KMS key with key_spec: SYMMETRIC_DEFAULT:

{
  "control_id": "3.2.4",
  "status": "pass",
  "evidence": [
    {
      "type": "aws-ec2",
      "instance_id": "i-1a2b3c4d5e6f",
      "encryption_enabled": true,
      "kms_key_id": "arn:aws:kms:us-east-1:123456789012:key/abcdef12-3456-7890-abcd-ef1234567890",
      "key_spec": "SYMMETRIC_DEFAULT"
    },
    {
      "type": "aws-kms",
      "key_id": "arn:aws:kms:us-east-1:123456789012:key/abcdef12-3456-7890-abcd-ef1234567890",
      "validation": "fips140-2",
      "status": "valid"
    }
  ]
}

If any instance lacks encryption, or the key spec is AWS_DEFAULT instead of SYMMETRIC_DEFAULT, the control fails.

Managing Control State and Change

Controls are not static. Their state evolves with system changes. Each control tracks its implementation, verification, and evidence over time. The controls.json file is updated after every successful pipeline run.

When a control fails, the system generates a ticket entry in the issue tracker. The ticket includes a link to the full report and a summary of the failure:

Title: PCI DSS v4 Control 1.1.1 Failed - SSH Configuration Mismatch

Status: Failed
Control ID: 1.1.1
Last Run: 2024-03-15T14:23:11Z
Report URL: https://ci.example.com/verify/1.1.1

Failure Summary:
- Task "Ensure SSH is configured" failed on host db01.example.com
- Error: "Failed to apply SSH configuration: /etc/ssh/sshd_config not found"
- Expected: /etc/ssh/sshd_config
- Actual: /etc/ssh/sshd_config.bak

Evidence:
- Log output:
  TASK [Ensure SSH is configured] ******************************************************************
  failed: [db01.example.com] (item=sshd_config) => {
    "changed": false,
    "failed": true,
    "msg": "Failed to apply SSH configuration: /etc/ssh/sshd_config not found",
    "item": "sshd_config"
  }
- Host inventory:
  - db01.example.com: SSH config file missing
  - web01.example.com: OK
  - web02.example.com: OK

The system automatically assigns the ticket to the team responsible for system hardening. When the fix is merged, the CI pipeline re-verifies the control and updates the report.

Handling Control Dependencies and Interactions

Some controls depend on others. For example, control 4.1.3 (Secure configuration of payment application) requires that control 3.2.4 (Encryption at rest) is active and passing.

The controls.json file supports a dependencies array:

{
  "id": "4.1.3",
  "name": "Secure configuration of payment application",
  "dependencies": [
    "3.2.4",
    "1.2.5"
  ],
  "verification": {
    "check": "python3 check_payment_config.py",
    "interval": "hourly",
    "schedule": "0 * * * *"
  },
  "evidence": [
    {
      "type": "config",
      "path": "/etc/payment-app/config.yaml",
      "schema": "payment-app-schema.json",
      "valid": true
    }
  ]
}

The verification pipeline checks that all dependencies are passing before marking the control as active. If any dependency fails, the control is marked as pending and the report includes the dependency status.

Detecting Silent Failures and Edge Cases

Several silent failures are common in PCI DSS v4.0 implementations:

Final Verification and Audit Readiness

At the end of each compliance cycle, the system generates a compliance audit package that includes:

The summary.json file is used to generate audit-ready dashboards and reports.

{
  "cycle": "2024-Q1",
  "start": "2024-01-01",
  "end": "2024-03-31",
  "total_controls": 42,
  "passing_controls": 39,
  "failing_controls": 3,
  "control_summary": {
    "1.1.1": "pass",
    "3.2.4": "pass",
    "4.1.3": "fail",
    "5.3.7": "pending"
  },
  "failure_details": [
    {
      "control_id": "4.1.3",
      "reason": "Dependency 3.2.4 failed",
      "evidence": [
        "https://ci.example.com/verify/3.2.4"
      ]
    }
  ]
}

This package is stored in a versioned artifact repository and is the primary evidence for an external audit.

Conclusion: The Practice of PCI DSS v4 Engineering Controls

PCI DSS v4.0 is not a document to be read, but a set of engineering practices to be executed. Every control is a living artifact, verified automatically, tracked continuously, and documented precisely. The foundation is controls.json, the engine is the CI/CD pipeline, and the outcome is a system that not only meets but proves compliance. The reader knows exactly how to design, implement, verify, and maintain PCI DSS v4.0 engineering controls—not by example, but by pattern.

This page was rewritten on 18 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.