# SSL/TLS 1.2+ is enforced for all cardholder data in transit

- **Pattern:** `ab-001180` (`ecommerce-pci.cardholder-data.tls-enforced`)
- **Severity:** critical
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001180
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001180)

## Why it matters

PCI-DSS 4.0 Req 4.2.1 prohibits transmitting PANs over end-user messaging technologies or unprotected open networks; Req 4.1 requires a targeted risk analysis for any TLS configuration below 1.2. Transmitting cardholder data over HTTP or TLS 1.0/1.1 exposes it to BEAST, POODLE, and CRIME class attacks — protocol-level attacks that allow passive decryption. CWE-319 (Cleartext Transmission) is exploitable at any network vantage point on the path. OWASP A02 (Cryptographic Failures) and NIST SC-8 both require encryption in transit as a baseline control. Incomplete enforcement — HSTS present but no redirect from port 80 — leaves a real downgrade path.

## Severity rationale

Critical because cardholder data sent over unencrypted or downgraded TLS connections can be intercepted in transit by any network-position attacker, directly violating PCI-DSS 4.0 Req 4.2 and exposing PANs.

## Remediation

Enforce HTTPS at the infrastructure layer, not just in application code. Set HSTS with a `max-age` of at least 31536000 and add an explicit HTTP-to-HTTPS redirect so there is no cleartext fallback path. For Next.js on Vercel, add to `next.config.js`:

```js
const securityHeaders = [
  { key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload' }
];
```

For nginx, set `ssl_protocols TLSv1.2 TLSv1.3;` and include a `listen 80` block that returns 301 before any content is served. Use Let's Encrypt via Certbot or your hosting provider's managed certificates (AWS ACM, Vercel auto-TLS) for automatic renewal — manual certificates expire silently.

## Detection

- **ID:** `tls-enforced`
- **Severity:** `critical`
- **What to look for:** Count all TLS/HTTPS enforcement signals across the project. Check for at least 1 of: HSTS header configuration (`Strict-Transport-Security`), HTTP-to-HTTPS redirect middleware, `ssl_protocols` directive in nginx/proxy config, or hosting platform HTTPS enforcement (`vercel.json`, `netlify.toml`). Count certificate management references (certbot, acme, Let's Encrypt, ACM). Enumerate all reverse proxy or load balancer configs and check TLS version settings.
- **Pass criteria:** At least 2 of the following TLS enforcement signals are present: (1) HSTS header with `max-age` of at least 31536000, (2) HTTP-to-HTTPS redirect in middleware or proxy, (3) `ssl_protocols TLSv1.2 TLSv1.3` or equivalent in proxy config, (4) hosting platform with automatic HTTPS (Vercel, Netlify). Certificate management is in place (at least 1 reference to auto-renewal or managed certificates). Report the count of enforcement signals found.
- **Fail criteria:** Fewer than 2 TLS enforcement signals found, or TLS version explicitly allows < 1.2, or certificates are self-signed without documented understanding of risk.
- **Skip (N/A) when:** Project is development-only (no deployment config, no hosting platform config, no payment routes) or uses no network communication.
- **Detail on fail:** Specify the TLS issue. Example: `"Only 1 TLS signal found (HSTS header). No HTTP-to-HTTPS redirect configured. TLS enforcement incomplete."` or `"Reverse proxy in nginx.conf accepts TLS 1.0 via ssl_protocols TLSv1 TLSv1.1 TLSv1.2"`
- **Remediation:** Enforce HTTPS site-wide. For Next.js on Vercel:

  ```json
  // vercel.json
  {
    "env": {
      "NEXT_PUBLIC_ENFORCE_HTTPS": "true"
    },
    "headers": [
      {
        "source": "/(.*)",
        "headers": [
          {
            "key": "Strict-Transport-Security",
            "value": "max-age=63072000; includeSubDomains; preload"
          }
        ]
      }
    ]
  }
  ```

  For Express/Node:
  ```js
  app.use((req, res, next) => {
    if (req.header('x-forwarded-proto') !== 'https') {
      res.redirect(`https://${req.header('host')}${req.url}`);
    } else {
      next();
    }
  });

  // Or use middleware like helmet
  import helmet from 'helmet';
  app.use(helmet.hsts({ maxAge: 63072000 }));
  ```

  For nginx:
  ```nginx
  server {
    listen 80;
    return 301 https://$server_name$request_uri;
  }
  server {
    listen 443 ssl http2;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_certificate /etc/ssl/certs/cert.pem;
    ssl_certificate_key /etc/ssl/private/key.pem;
  }
  ```

  Use Let's Encrypt (via Certbot) or your hosting provider's managed certificates (Vercel, AWS ACM) for automatic renewal.

---

## External references

- pci-dss Req 4.2
- pci-dss Req 4.1
- cwe CWE-319
- owasp A02
- nist SC-8

Taxons: cryptography-and-secrets, regulatory-conformance

HTML version: https://auditbuffet.com/patterns/ab-001180
