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

A04:2025 - Cryptographic Failures
A07:2025 - Authentication Failures (secondary — certificate validation is also a server authentication failure)

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 where required: Not enabling or enforcing Certificate Revocation Lists (CRL) or OCSP when your platform, policy, or threat model requires revocation checking

Language-Specific Guidance

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

  • C# / .NET - HttpClientHandler callback bypass, DangerousAcceptAnyServerCertificateValidator, ServicePointManager, custom CA validation
  • Go - crypto/tls InsecureSkipVerify, custom RootCAs, mutual TLS
  • Java - empty X509TrustManager, trust-all HostnameVerifier, SSLContext bypass, HttpClient secure 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 where enforced: Check Certificate Revocation List (CRL) or OCSP if revocation checking is required and supported by your client stack
  • Hostname matches certificate: Verify hostname matches the Subject Alternative Name (SAN); treat Common Name-only certificates as legacy certificates to replace, not as the target compatibility model
  • 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 pinning only for high-security cases

  • 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 public key/SPKI hashes with backup pins and a rotation plan; avoid pinning short-lived leaf certificates without operational support

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 if CRL/OCSP checking is enabled by your client stack and policy
  • Re-scan with security scanner to confirm the issue is resolved

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 a host whose certificate is issued for a different name
curl https://wrong.host.badssl.com/

# 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; only expected to fail when revocation checking is enabled and supported)

Self-signed, expired, and wrong-hostname endpoints should fail validation. Revoked-certificate tests should fail only when revocation checking is explicitly enabled and supported by the runtime.

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)

  • Phase 1: Inventory endpoints and run validation probes outside the live request path using openssl s_client:
    # Probe a single endpoint and report certificate or hostname validation errors
    openssl s_client -connect api.example.com:443 -servername api.example.com \
      -verify_hostname api.example.com -verify_return_error < /dev/null 2>&1 \
      | grep -E "Verify return code|subject|issuer"
    

    See the language-specific pages for programmatic probing patterns.

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

    For internal or partner CAs, add the CA certificate to your application's trust store rather than disabling validation. See the language-specific pages for code-level patterns (Python ssl.create_default_context, Java KeyStore/TrustManagerFactory, C# CustomRootTrust or OS trust stores, Go tls.Config.RootCAs).

  • Phase 4: Enable enforcement and remove all bypass flags

    Remove all verify=False, rejectUnauthorized: false, trust-all callbacks, and empty TrustManager implementations. Certificate validation errors should propagate as exceptions, not be swallowed.

Environment-Specific Configuration

Development and staging environments should use a trusted internal CA certificate rather than disabled validation. Configure the CA bundle path via an environment variable and point it to your dev CA file:

# Set a dev CA bundle path - use a real CA cert, never disable verification
export DEV_CA_BUNDLE=/etc/ssl/certs/dev-ca.pem

Add Specific Certificates to Trust Store

Add CA certificates to the system trust store on Linux:

# Add corporate CA to system trust store (Debian/Ubuntu)
cp corporate-ca.crt /usr/local/share/ca-certificates/
update-ca-certificates

For application-level trust stores, refer to the language-specific pages: C# uses CustomRootTrust on supported .NET versions or OS trust stores, Java uses a KeyStore-backed TrustManagerFactory, Python uses ssl.create_default_context with load_verify_locations, and Go uses tls.Config.RootCAs.

Rollback Procedures

If certificate validation breaks critical services:

  • Trust the correct CA quickly: add the missing internal or partner CA to the application or system trust store.
  • Route to a known-good endpoint: temporarily move traffic to an endpoint with a valid certificate.
  • Fail closed for sensitive traffic: do not restore verify=False, rejectUnauthorized: false, or trust-all validators as a rollback.

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 development and staging CA bundles work without disabling validation
  • Document all endpoints with custom certificates

Kubernetes and Infrastructure Contexts

Certificate validation bypasses are not limited to application code. Kubernetes configuration files and tooling introduce additional vectors that are frequently overlooked.

kubeconfig insecureSkipTLSVerify

# VULNERABLE - kubeconfig with TLS verification disabled
apiVersion: v1
clusters:
- cluster:
    insecure-skip-tls-verify: true  # NEVER use in production
    server: https://kubernetes.api.example.com
  name: my-cluster

Setting insecure-skip-tls-verify: true in a kubeconfig file disables TLS verification for all API server communication, exposing service account tokens and all cluster traffic to MITM attacks. This is commonly added as a quick fix for certificate errors and left in permanently.

Secure alternative: Provide the cluster CA certificate instead:

# SECURE - kubeconfig with proper CA bundle
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: <base64-encoded-CA-cert>
    server: https://kubernetes.api.example.com
  name: my-cluster

kubectl --insecure-skip-tls-verify flag

# VULNERABLE - disabling verification via CLI flag or in automation scripts
kubectl --insecure-skip-tls-verify get pods

The --insecure-skip-tls-verify flag should never be used in CI/CD pipelines or automation. Configure the cluster CA correctly in kubeconfig instead.

In-Cluster Applications

Applications running as pods should use the service account CA bundle automatically mounted at /var/run/secrets/kubernetes.io/serviceaccount/ca.crt rather than disabling certificate validation when calling the Kubernetes API. See the Go and Python language-specific pages for code-level guidance.

Additional Resources