Skip to content

CWE-295: Improper Certificate Validation

Overview

Improper certificate validation occurs when an application fails to correctly verify the authenticity of SSL/TLS certificates during secure connections. This undermines the entire purpose of HTTPS by allowing attackers to intercept encrypted communications (man-in-the-middle attacks), impersonate legitimate servers, and steal credentials or sensitive data in transit.

SSL/TLS certificates serve two critical purposes: authentication (prove the server is who it claims to be) and encryption (establish a secure encrypted channel). When certificate validation is disabled or improperly implemented, encryption alone is insufficient - you may be encrypting data to an attacker's server.

OWASP Classification

A07:2025 - Authentication Failures

Risk

Certificate validation failures create severe security exposures:

  • Man-in-the-middle (MITM) attacks: Attackers intercept all encrypted traffic
  • Credential theft: Username/password pairs transmitted to attacker
  • Session hijacking: Session tokens captured and reused
  • Data exfiltration: Sensitive data read from encrypted connections
  • API key exposure: Authentication tokens for external services stolen
  • Payment data theft: Credit card numbers and financial information compromised
  • Compliance violations: Fails PCI-DSS, SOC 2, HIPAA encryption requirements

MITM attacks are trivial to execute on public WiFi networks, and certificate validation failures make them undetectable to users.

Common Validation Failures

Certificate validation failures typically fall into these categories:

  • Disabling validation entirely: Accepting all certificates regardless of validity
  • Hostname verification bypass: Not checking hostname matches certificate
  • Chain verification failure: Not verifying certificate chain to trusted root CA
  • Ignoring certificate status: Accepting expired or revoked certificates
  • Missing revocation checks: Not checking Certificate Revocation Lists (CRL) or OCSP

Language-Specific Guidance

For detailed, language-specific remediation guidance including vulnerable and secure code patterns:

  • JavaScript/Node.js - https module rejectUnauthorized, axios httpsAgent, node-fetch configuration, TLS module, certificate pinning
  • Python - requests verify=False, urllib3 cert_reqs, SSL context configuration, httpx, aiohttp validation

Remediation Steps

Core principle: TLS must validate certificates correctly (chain + hostname); never disable verification in production.

Locate the improper certificate validation in your code

  • Review the flaw details to identify where certificate validation is disabled or bypassed
  • Identify HTTPS connection points: API calls to external services, database connections over SSL/TLS, email servers, webhooks
  • Search for dangerous patterns: "verify=False", "rejectUnauthorized: false", "TrustAllCertificates", "InsecureTrustManager"
  • Check configuration files: application.properties, config files for certificate validation settings

Use default certificate validation (Primary Defense)

  • Enable platform default validation: Use standard HTTPS libraries with default settings (they validate correctly by default)
  • Let the platform handle validation: Don't implement custom certificate validation logic
  • Trust OS certificate store: Rely on operating system's trusted root certificate authorities
  • Remove bypass code: Delete any "verify=False", "rejectUnauthorized: false", custom TrustManagers that accept all certificates
  • Never disable validation: Even in development, use self-signed certificates properly instead of disabling validation

Validate certificate chain and hostname properly

If custom validation is required, verify ALL aspects:

  • Certificate signed by trusted CA: Check chain to root certificate authority
  • Certificate not expired: Validate notBefore and notAfter dates
  • Certificate not revoked: Check Certificate Revocation List (CRL) or OCSP
  • Hostname matches certificate: Verify hostname matches Subject Alternative Name (SAN) or Common Name (CN)
  • Certificate purpose matches usage: Ensure certificate is for Server Authentication
  • Strong signature algorithm: Ensure SHA-256 or better (not MD5/SHA-1)

Remove development bypass code and use certificate pinning for high-security

  • Remove development shortcuts: Delete "trust all certificates" classes, "ignore SSL errors" flags, custom TrustManagers
  • Search codebase: Grep for "trust all", "accept all", "disable validation", "InsecureTrustManager"
  • Remove environment bypass: Delete environment variables or debug flags that skip certificate checks
  • Certificate pinning (optional): For critical connections, pin expected certificate public key hash, reject even valid-but-unexpected certificates

Monitor and audit SSL/TLS connections

  • Review code for all HTTPS connection points and their validation configuration
  • Log SSL/TLS handshake failures (may indicate MITM attempts or expired certificates)
  • Monitor certificate expiration dates and renew before expiry
  • Alert on certificate validation bypasses if found in code reviews
  • Use static analysis tools to detect disabled validation

Test the certificate validation fix thoroughly

  • Test with valid certificate from trusted CA (should connect successfully)
  • Test with self-signed certificate (should reject unless explicitly trusted)
  • Test with expired certificate (should reject)
  • Test with wrong hostname (should reject due to hostname mismatch)
  • Test with revoked certificate (should reject if CRL/OCSP checking enabled)
  • Re-scan with security scanner to confirm the issue is resolved

1. Test with Valid Certificate

# Normal HTTPS connection should work

curl https://www.google.com

# Expected: Success (200 OK)

Your application should successfully connect to properly configured HTTPS servers.

Test with Self-Signed Certificate

# Connect to server with self-signed cert (common in testing)

curl https://self-signed.badssl.com/

# Expected: Certificate verification error

Expected result: Connection should fail with certificate verification error (unless you've explicitly added the CA to trust store).

Test with Expired Certificate

# Connect to server with expired cert

curl https://expired.badssl.com/

# Expected: Certificate has expired error

Expected result: Connection refused due to expired certificate.

Test Hostname Mismatch

# Connect to IP address with cert issued for domain name

# Expected: Hostname verification failure

Expected result: Connection refused due to hostname mismatch.

Automated Testing

Use test sites:

  • https://badssl.com/ (various certificate issues)
  • https://self-signed.badssl.com/ (self-signed)
  • https://expired.badssl.com/ (expired)
  • https://wrong.host.badssl.com/ (hostname mismatch)
  • https://revoked.badssl.com/ (revoked certificate)

All should fail validation in your application

Code Review Verification

Verify:

  • No TrustAllCertificates implementations remain
  • No certificate validation bypass flags
  • Default validation is used
  • No SSL error suppression
  • Production config differs from development

Migration Considerations

IMPORTANT: Enabling certificate validation may break integrations with self-signed certificates or expired certificates.

What Breaks

  • Dev/staging environments: Often use self-signed certificates
  • Internal APIs: May have self-signed or corporate CA certificates
  • Legacy systems: Old servers with expired certificates
  • Partner integrations: Third-party APIs with certificate issues
  • Testing environments: Mock servers with invalid certificates

Migration Approach

Gradual Rollout (Recommended)

  1. Phase 1: Log validation failures without blocking

      def verify_certificate(hostname, cert):
          valid = check_certificate_validity(cert)
          if not valid:
              logger.warning(f"Certificate validation failed for {hostname}: {cert}")
              # Don't block yet - log only
          return True  # Allow connection
    
  2. Phase 2: Identify problematic endpoints from logs

    # Analyze logs to find failing certificates
    grep "Certificate validation failed" app.log | sort | uniq -c
    
  3. Phase 3: Fix certificate issues or add trusted CAs

   # Add corporate CA to trust store
   import certifi
   import ssl

   def create_ssl_context():
       context = ssl.create_default_context(cafile=certifi.where())

       # Add corporate CA certificate
       context.load_verify_locations('corporate-ca.pem')

       return context
  1. Phase 4: Enable enforcement
   def verify_certificate(hostname, cert):
       if not check_certificate_validity(cert):
           logger.error(f"Blocked connection to {hostname}: invalid certificate")
           raise SSLError("Certificate validation failed")

Environment-Specific Configuration

import os
import requests

def get_api_data(url):
    # Only disable verification in development
    verify_ssl = os.getenv('ENVIRONMENT') != 'development'

    if not verify_ssl:
        logger.warning(f"SSL verification disabled for {url} (dev mode)")

    response = requests.get(url, verify=verify_ssl)
    return response.json()

Add Specific Certificates to Trust Store

# Python

import ssl
import certifi

ssl_context = ssl.create_default_context(cafile=certifi.where())
ssl_context.load_verify_locations('internal-ca.pem')

# Java

import java.security.KeyStore;
import javax.net.ssl.TrustManagerFactory;

// Load custom trust store
KeyStore trustStore = KeyStore.getInstance("JKS");
trustStore.load(new FileInputStream("truststore.jks"), password);

TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init(trustStore);

Rollback Procedures

If certificate validation breaks critical services:

  1. Feature flag rollback:
   if config.get('STRICT_CERT_VALIDATION'):
       verify_certificate(hostname, cert)
   else:
       logger.warning("Certificate validation bypassed (feature flag)")
  1. Allowlist specific hosts temporarily:
   CERT_VALIDATION_EXEMPT = [
       'legacy-api.internal.company.com',
       'old-partner-api.example.com'
   ]

   def should_verify_cert(hostname):
       return hostname not in CERT_VALIDATION_EXEMPT
  1. Add trusted CA quickly:
   # Add corporate CA to system trust store
   cp corporate-ca.crt /usr/local/share/ca-certificates/
   update-ca-certificates

Testing Recommendations

  • Test connections to production APIs (valid certificates)
  • Test rejection of self-signed certificates
  • Test rejection of expired certificates
  • Test hostname mismatch detection
  • Test corporate CA certificates work
  • Test dev environment bypass still works
  • Document all endpoints with custom certificates

Certificate Testing Checklist:

import pytest
import requests

def test_valid_certificate_works():
    # Should connect successfully
    response = requests.get('https://www.google.com', verify=True)
    assert response.status_code == 200

def test_self_signed_certificate_rejected():
    # Should fail with SSL error
    with pytest.raises(requests.exceptions.SSLError):
        requests.get('https://self-signed.badssl.com/', verify=True)

def test_expired_certificate_rejected():
    # Should fail with SSL error
    with pytest.raises(requests.exceptions.SSLError):
        requests.get('https://expired.badssl.com/', verify=True)

def test_corporate_ca_works():
    # Add corporate CA
    context = create_ssl_context_with_corporate_ca()

    # Should work with corporate cert
    response = requests.get('https://internal-api.company.com', verify=context)
    assert response.status_code == 200

Dynamic Scan Guidance

For guidance on remediating this CWE when detected by dynamic (DAST) scanners:

Additional Resources