Skip to content

CWE-295: Improper Certificate Validation

Overview

This guidance helps you interpret and remediate findings from DAST (Dynamic Application Security Testing) tools. The scanner detected that the application accepted invalid, expired, or self-signed TLS/SSL certificates when making outbound HTTPS connections. Evidence includes successful responses from endpoints using man-in-the-middle proxies with invalid certificates, acceptance of hostname mismatches, or communication proceeding despite certificate validation errors. This is often detected when the application makes backend API calls that don't properly validate certificates.

Analyzing the Dynamic Scan Result

What the DAST Scanner Found

DAST may surface this weakness indirectly (e.g., via a feature that proxies to an upstream over HTTPS or via observable behavior under interception), but confirmation typically requires tracing the application’s outbound TLS client configuration.

When reviewing your security scan results, you'll see:

HTTP Request Details

  • URL and endpoint that triggered the finding
  • HTTP method (GET, POST, etc.)
  • Query parameters or form data with test payloads
  • Request headers and body content

HTTP Response Evidence

  • Response showing the vulnerability manifestation
  • Evidence that certificate chain/hostname validation was not enforced
  • Runtime behavior indicators

Attack Vector

  • Which parameter or input is vulnerable
  • Type of exploitation possible
  • Context where the vulnerability appears

Mapping DAST Findings to Source Code

Find the Vulnerable Endpoint

Search for HTTPS client connections:

# Search for HTTP client code
grep -r "requests.get" src/          # Python requests
grep -r "https.get" src/             # Node.js https
grep -r "HttpClient" src/            # Java/C#
grep -r "verify.*False" src/         # Disabled verification
grep -r "SSL_VERIFY" src/

Locate TLS/SSL Client Code

Common patterns to search for:

  • Python: requests.get(url, verify=False), ssl._create_unverified_context()
  • Node.js: https.get({rejectUnauthorized: false}), process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
  • Java: SSLContext, TrustManager, custom certificate validation
  • C#/.NET: ServerCertificateValidationCallback, ServicePointManager
  • PHP: curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false)

Find Certificate Validation

Search for disabled verification:

# Find disabled SSL verification
grep -r "verify=False" src/
grep -r "rejectUnauthorized.*false" src/
grep -r "CURLOPT_SSL_VERIFYPEER.*false" src/

Trace to Vulnerable Operation

Look for improper certificate handling:

  • Disabled verification: verify=False, rejectUnauthorized: false
  • Custom trust managers: Accepting all certificates
  • Self-signed certificates: In production without proper validation
  • Expired certificates: Not checked
  • Hostname mismatch: Not verified

Remediation

Core principle: Never establish a TLS connection unless the peer's certificate chain and hostname are validated against a trusted root store; authentication of the remote endpoint must be performed by the TLS stack, not by application logic.

→ For comprehensive remediation guidance, see Static CWE-295 Guidance

The static guidance provides detailed remediation steps for all languages. If you need language-specific examples:

Verification and Follow-Up Testing

After applying the fix:

Reproduce the Vulnerability

# Check code for disabled verification
grep -r "verify.*False" src/
grep -r "rejectUnauthorized.*false" src/

Trigger the application feature that makes an outbound HTTPS request to a self-signed/invalid-cert upstream and verify the request fails. (Using badssl.com endpoints is a convenient upstream for this test.)

Verify the Fix

  • Confirm certificate validation enabled (no verify=False)
  • Verify hostname matching enforced
  • Check trusted CA certificates used
  • Ensure certificate expiry checked
  • Test that invalid certificates are rejected
  • Hostname mismatching is detected and rejected

Test Edge Cases

# Self-signed certificates should be rejected
curl https://self-signed.badssl.com/

# Expired certificates should be rejected
curl https://expired.badssl.com/

# Wrong hostname should be rejected
curl https://wrong.host.badssl.com/

# Untrusted root should be rejected
curl https://untrusted-root.badssl.com/

# Or use browser DevTools Network tab to copy as cURL

Re-run DAST Scanner

Run your dynamic scanner again on the fixed endpoint to confirm remediation.

Additional Resources