Certificate validation for payment processor API endpoints
Why it matters
CWE-295 (improper certificate validation) and CWE-319 (cleartext transmission) both apply when rejectUnauthorized: false is set on payment processor connections. This flag tells Node.js to accept any certificate — including expired, self-signed, or attacker-controlled certificates — making TLS meaningless. An attacker positioned between your server and the payment processor can intercept payment requests, capture API keys, and manipulate transaction amounts without detection. PCI-DSS 4.0 Req-4.2 requires that cardholder data be protected during transmission, which presupposes that the TLS certificate chain is actually verified. NIST SC-8 reinforces this. This vulnerability is commonly introduced as a development workaround and accidentally shipped to production.
Severity rationale
High because a single `rejectUnauthorized: false` in a payment API call completely nullifies TLS and enables undetectable MITM attacks against financial transactions.
Remediation
Remove every instance of rejectUnauthorized: false from payment processor calls. Modern Stripe and Plaid SDKs validate certificates by default — use the SDK rather than raw fetch or axios for payment API calls:
// CORRECT — Stripe SDK validates certs automatically
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
// If you must use a raw HTTP client, never override cert validation:
const response = await fetch('https://api.stripe.com/v1/charges', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.STRIPE_SECRET_KEY}` },
// No agent override, no rejectUnauthorized
});
// NEVER do this — even in dev:
// const agent = new https.Agent({ rejectUnauthorized: false });
Search the codebase for rejectUnauthorized and NODE_TLS_REJECT_UNAUTHORIZED before each production deploy. If a dev environment requires connecting to a self-signed cert, use a CA bundle override instead of disabling validation entirely.
Detection
- ID:
certificate-validation - Severity:
high - What to look for: Count all HTTP client calls to payment processors (fetch, axios, node-fetch). For each, verify certificate validation is enabled — quote the actual
rejectUnauthorizedsetting found. Enumerate all instances of disabled SSL verification. AnyrejectUnauthorized: falsedoes not count as pass — do not pass if any API call disables certificate validation. - Pass criteria: At least 100% of payment processor API calls use HTTPS with certificate validation enabled (default in modern libraries). Count all external API calls — 0 must have disabled certificate verification. Report the count even on pass (e.g., "4 of 4 payment API calls use HTTPS with certificate validation, 0 instances of rejectUnauthorized: false").
- Fail criteria: Any certificate verification is explicitly disabled (even 1 instance of
rejectUnauthorized: false), or any payment API call uses HTTP. - Skip (N/A) when: Payment processor is handled entirely client-side (e.g., Stripe Elements) with no server-side API calls — cite the actual integration pattern found.
- Detail on fail:
"1 of 3 payment API calls has rejectUnauthorized: false — certificate validation disabled in src/services/payment.ts:42"or"2 of 4 Stripe calls use HTTP instead of HTTPS" - Cross-reference: Check
finserv-encryption.data-in-transit.tls-12-minimumfor TLS version configuration. - Remediation:
- Verify HTTPS usage:
// Stripe SDK automatically validates certificates import Stripe from 'stripe'; const stripe = new Stripe(process.env.STRIPE_SECRET_KEY); const payment = await stripe.charges.create({ /* ... */ }); // For custom HTTP calls: const https = require('https'); const options = { hostname: 'api.stripe.com', port: 443, path: '/v1/charges', method: 'POST', rejectUnauthorized: true, // Ensure this is true (default) agent: new https.Agent({ rejectUnauthorized: true }) }; - Never disable certificate verification:
// WRONG - DO NOT USE const response = await fetch('https://api.stripe.com/...', { rejectUnauthorized: false // Dangerous! }); // CORRECT const response = await fetch('https://api.stripe.com/...'); // Default validation
- Verify HTTPS usage:
External references
- cwe · CWE-295 — Improper Certificate Validation
- cwe · CWE-319 — Cleartext Transmission of Sensitive Information
- owasp:2021 · A02 — Cryptographic Failures
- nist:rev5 · SC-8 — Transmission Confidentiality and Integrity
- pci-dss:4.0 · Req-4.2 — PAN protected with strong cryptography during transmission
Taxons
History
- 2026-04-18·v1.0.0·Initial import from finserv-encryption·automated