CWE-296: Improper Following of a Certificate's Chain of Trust
Overview
Improper following of a certificate's chain of trust occurs when applications fail to verify that an X.509 certificate chains properly to a trusted root Certificate Authority (CA). This allows attackers to use self-signed certificates, certificates signed by untrusted CAs, incomplete chains, or chains with invalid intermediate CA certificates to impersonate legitimate servers in man-in-the-middle (MITM) attacks. The vulnerability commonly arises from disabled certificate validation during development that remains in production, custom TLS implementations that skip chain verification, incorrect trust store configuration, or pinning logic that pins a certificate before validating the complete chain.
OWASP Classification
A04:2025 - Cryptographic Failures
Risk
High: Enables man-in-the-middle attacks where attackers intercept encrypted communications, steal credentials, session tokens, sensitive data, and inject malicious content. Particularly dangerous on public Wi-Fi, compromised networks, or when attackers control network infrastructure (DNS, routing).
Remediation Steps
Core principle: Always validate the complete certificate trust chain to a known trusted root CA or explicitly configured trust anchor; do not disable certificate validation or trust unverified certificates in application code.
Enable Full Certificate Chain Validation
Ensure the TLS/SSL library validates:
- Certificate chains to a trusted root CA
- Certificate is not self-signed unless it is the trusted root or an explicitly configured trust anchor
- Intermediate certificates are included and valid
- Intermediate CA certificates have appropriate Basic Constraints and key usage
- Certificate has not expired
- Certificate revocation status is checked when required by your client stack, PKI, or policy
Use Proper TLS Configuration
# VULNERABLE - disables chain validation
import requests
requests.get(url, verify=False) # NEVER do this!
# SECURE - validates full chain
requests.get(url, verify=True) # Or omit verify (True is default)
//VULNERABLE - Node.js disables validation
const https = require('https');
https.get(url, {
rejectUnauthorized: false // DANGEROUS!
});
// SECURE - proper validation
https.get(url, {
rejectUnauthorized: true // Or omit (true is default)
});
Pin Public Keys for Critical Connections (Advanced)
For high-security scenarios, pin expected public key/SPKI hashes only after the normal chain and hostname validation succeeds. Keep backup pins and a rotation process; do not pin an otherwise unvalidated certificate.
Use System Trust Store
Rely on operating system or language-provided trust stores:
- Python:
certifi,ssl.create_default_context() - Node.js: default root certificates,
NODE_EXTRA_CA_CERTS, or a carefully scopedcaoption for internal clients - Java: JRE cacerts keystore
- .NET: Windows Certificate Store
Test Certificate Validation
Verify your implementation rejects invalid certificates:
# Test with self-signed certificate (should fail)
curl https://self-signed.badssl.com/
# Test with expired certificate (should fail)
curl https://expired.badssl.com/
# Test with wrong host (should fail)
curl https://wrong.host.badssl.com/