# TLS certificates validated for outbound connections

- **Pattern:** `ab-000399` (`api-security.design-security.tls-validation`)
- **Severity:** info
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000399
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000399)

## Why it matters

Disabling TLS certificate validation (`rejectUnauthorized: false`) makes every outbound HTTPS connection trivially man-in-the-middle-able. CWE-295 (Improper Certificate Validation) is the classification, and it's particularly dangerous for payment processor integrations, OAuth token exchanges, and inter-service calls where data sensitivity is highest. OWASP A02 2021 (Cryptographic Failures) and API Security Top 10 2023 API8 cover TLS misuse. Developers add `rejectUnauthorized: false` to work around local cert issues and commit it to production — a search across GitHub shows this pattern in thousands of repositories. An attacker on the same network path can intercept the full request and response, including auth tokens and payment data.

## Severity rationale

Info severity because exploiting disabled TLS validation requires network-path access, but the consequence — full MITM interception of sensitive API calls — is critical.

## Remediation

Remove every `rejectUnauthorized: false` from production code. For local development with self-signed certificates, set the flag conditionally on `NODE_ENV` and never commit the unconditional override.

```ts
// Bad — never do this in production
const agent = new https.Agent({ rejectUnauthorized: false })
const res = await fetch('https://api.stripe.com/v1/charges', { agent })

// Good — explicit validation (this is also the default, no option needed)
const res = await fetch('https://api.stripe.com/v1/charges')

// Acceptable for local dev only — gated on environment
const agent = new https.Agent({
  rejectUnauthorized: process.env.NODE_ENV !== 'development'
})
```

For internal services with private CAs, provide the CA certificate explicitly rather than disabling validation: `new https.Agent({ ca: fs.readFileSync('internal-ca.pem') })`.

## Detection

- **ID:** `tls-validation`
- **Severity:** `info`
- **What to look for:** Enumerate every relevant item. Check how the application makes outbound HTTPS requests (to payment processors, external APIs, webhooks). Verify that TLS certificate validation is enabled (not disabled with rejectUnauthorized: false).
- **Pass criteria:** All outbound HTTPS requests validate TLS certificates. Certificate validation is not disabled. Critical integrations use certificate pinning.
- **Fail criteria:** Outbound HTTPS requests disable certificate validation (rejectUnauthorized: false in Node.js), or do not validate certificates.
- **Skip (N/A) when:** The application makes no outbound HTTPS requests.
- **Detail on fail:** `"Outbound HTTPS requests have certificate validation disabled (rejectUnauthorized: false)"` or `"Stripe integration disables TLS validation — vulnerable to MITM attacks"`
- **Remediation:** Enable TLS validation:

  ```ts
  // Bad: Disabled validation
  https.request(url, { rejectUnauthorized: false }, callback)

  // Good: Default (validates by default)
  https.request(url, callback)

  // Good: Explicit validation
  https.request(url, { rejectUnauthorized: true }, callback)

  // For critical integrations, use certificate pinning
  const https = require('https')
  const fs = require('fs')

  const agent = new https.Agent({
    ca: fs.readFileSync('ca-cert.pem'),
    cert: fs.readFileSync('client-cert.pem'),
    key: fs.readFileSync('client-key.pem')
  })

  https.request(url, { agent }, callback)
  ```

---

## External references

- cwe CWE-295
- cwe CWE-326
- owasp A02
- owasp API8

Taxons: cryptography-and-secrets

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