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.