CWE-295 is short to state: the product does not validate a certificate, or validates it incorrectly. The damage is not short. TLS only protects a connection if the client checks who is on the other end, and this weakness removes that check.
It affects web back ends calling other services, mobile apps, IoT firmware, update mechanisms and any library that opens a TLS connection. MITRE lists platforms as not language specific, web based and mobile.
How it happens
Most real cases fall into a few buckets.
Verification switched off. A developer hits a self-signed certificate error in testing, sets a flag to make it go away, and the flag ships. Every one of these lines does the same damage:
# Python requests: certificate checks disabled
requests.get("https://api.internal.example/data", verify=False)
// Node.js: accept any certificate
const agent = new https.Agent({ rejectUnauthorized: false });
// Go: skip all verification
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
# curl: -k means insecure
curl -k https://updates.example/firmware.bin
Verification written wrong. The check exists but has a hole. MITRE's observed examples include a verification function that trusts certificate chains in which the last certificate is self-signed, and the Apple "goto fail" bug.
Library defaults. A Go framework for robotics, drones and IoT devices skipped verification of root CA certificates by default.
Real examples
MITRE lists these observed CVEs:
- CVE-2019-12496: a Go framework for robotics, drones and IoT devices skips verification of root CA certificates by default.
- CVE-2014-1266: a chain in which an incorrect goto in an Apple SSL product bypasses certificate validation and allows an adversary-in-the-middle attack.
- CVE-2021-22909: a router's firmware update procedure uses curl with the insecure
-koption, which disables certificate validation and allows adversary-in-the-middle compromise with a malicious firmware image. - CVE-2008-4989: a verification function trusts certificate chains in which the last certificate is self-signed.
The router case shows how bad it gets. A firmware update is code your device will run. If the download channel is not authenticated, whoever sits in the path chooses the code.
What an attacker gains
When a certificate is invalid or malicious, MITRE says it may allow an attacker to spoof a trusted entity by interfering in the communication path between the host and client. The product might connect to a malicious host while believing it is connected to a trusted one. The listed impacts are integrity loss, authentication bypass, bypass of protection mechanisms and gaining privileges or assuming identity.
How to find it
MITRE's detection notes point to several approaches:
- Dynamic analysis with a man-in-the-middle tool. MITRE rates this highly cost effective. Put an intercepting proxy with its own certificate between your app and its server. If the app still connects, it is not validating.
- Web application scanners give partial coverage.
- Static analysis of binaries or bytecode, including disassemblers and weakness analysis, is useful for partial coverage when you have no source.
- Code search. Grep for
verify=False,rejectUnauthorized: false,InsecureSkipVerify,curl -k,--insecure, and custom trust managers or hostname verifiers that return true.
How to fix it
- Never disable verification in shipped code. If a test environment uses a private CA, add that CA to the trust store for that environment instead of turning checks off.
- Use the platform's default TLS validation rather than writing your own. Hand-rolled chain checks are where the subtle bugs live.
- Manage certificates carefully. MITRE advises that certificates should be carefully managed and checked so that data is encrypted with the intended owner's public key.
- If you pin certificates, validate fully first. MITRE says to ensure all relevant properties of the certificate, including the hostname, are fully validated before the certificate is pinned.
- Scan dependencies and base images too. A library that skips verification by default, like the Go framework above, brings the weakness into your product without a line of your own code. A free in-browser dependency scan needs no login, keeps the file on your machine and covers up to 100 dependencies. Reachability analysis then tells you whether your code actually calls the affected function.
# Fixed: default verification, private CA added explicitly
requests.get("https://api.internal.example/data", verify="/etc/ssl/certs/internal-ca.pem")
Why it keeps happening
Certificate errors are loud in development and silent in production. A developer sees a handshake failure against a staging server with a self-signed certificate, searches for the quickest way to make it stop, and finds the flag. The flag works, the ticket closes, and nothing in the running app tells anyone the check is gone.
Three habits make it worse.
First, the switch hides in a helper. A shared HTTP client wrapper with verification off gets reused by dozens of callers who never read its source.
Second, the exception is meant to be temporary. "Remove before release" comments survive for years. A code review that looks only at the diff will not catch a flag set long ago.
Third, the weak setting comes from a dependency. When a library skips root CA verification by default, as in the robotics framework above, every application built on it inherits the gap until someone notices.
A practical defense is to make the insecure setting visible. Add a lint or CI rule that fails the build on the search terms listed earlier, require an explicit, reviewed allowlist for any exception, and run the man-in-the-middle test against each build of any client that talks to a remote host.
Try it yourself
This demo walks through a dynamic web application scan, a partial-coverage detection method for this weakness.
Click through it at your own pace, or open it full screen.
Quick check
Is verify=False acceptable for internal services?
No. An internal network can still hold an attacker or a compromised host. Trust your own CA explicitly instead of trusting everything.
My app checks that the certificate is signed by a trusted CA. Is that enough?
Not by itself. The certificate also has to match the host you meant to reach (see CWE-297), be within its validity dates (CWE-298), and not be revoked (CWE-299).
What does a good test look like?
Point the client at a server presenting a certificate from an untrusted authority and confirm the connection fails. Then try one signed by a trusted authority but for a different hostname, and confirm that fails too.