Validating a certificate
More than a valid signature
Every time the clinic portal's server fetches results, it opens an HTTPS connection to results.lab.example. The lab's server presents a certificate chain, and the portal's HTTP client has to decide whether to continue. Alex's browser makes the same decision whenever it connects to the portal.
A chain that ends at a trusted root only shows that a trusted CA issued the certificate. The client still needs to know that the certificate is current, that it names the server it meant to reach, that it was issued for this use, and that the server actually holds the matching private key. Most of this is handled by the TLS library, but knowing what it checks makes its errors understandable and its configuration less mysterious.
The checks
For the portal's connection to the lab, validation includes these checks. Each one answers a different question, and passing one says nothing about the others.
| Check | Question it answers |
|---|---|
| Chain to a trust anchor | Does a path of valid signatures lead from this certificate to a root the client trusts? |
| Validity period | Is the current time between each certificate's not-before and not-after times? |
| Name | Does results.lab.example match a name in the subject alternative names? |
| Permitted use | Do the key usage and extended key usage allow TLS server authentication? |
| CA constraints | Is every issuer in the chain marked as a CA, within its path length and name constraints? |
| Revocation | Has the issuer said that any certificate in the chain should no longer be trusted? |
| Proof of the private key | Did the server sign the handshake with the key named in the certificate? |
The last check happens during the TLS handshake rather than by reading the certificate. It is what separates the lab's server from anyone who merely copied the lab's public certificate.
The name check depends on the client knowing which name it intended. An HTTP client connecting to a URL knows the hostname from the URL. Code that opens a TLS connection to an IP address or builds a socket by hand may not, and a missing expected name means the certificate for any site signed by a trusted CA would pass. Well-maintained libraries perform all of these checks by default when given a hostname.
Reading a failure
When a check fails, the client stops before sending any request data. These illustrative messages show the kinds of errors each failed check produces. Exact wording varies between libraries.
results.lab.example: certificate has expired
(not after: 2026-09-28 23:59:59 UTC)
results.lab.example: hostname mismatch
(certificate names: api.lab.example, www.lab.example)
internal-reports.harborclinic.example: unable to get local issuer certificate
(issuer: Harbor Clinic Internal CA)
The first error means the lab's certificate expired yesterday and was not renewed, which the lab needs to fix. The second means the client reached a server whose certificate does not cover the name it requested. That could be a misconfigured server, or it could be the wrong server entirely. The third means the client could not build a chain to any root it trusts. Here the issuer is the clinic's own private CA, which this client has not been configured to trust.
Checking revocation
A certificate can stop being trustworthy before it expires, most importantly when its private key is exposed. The CA can revoke it and publish that decision. Clients can learn about revocations through a certificate revocation list (CRL), a signed list of revoked serial numbers that clients download, or through the Online Certificate Status Protocol (OCSP), which asks the CA about one certificate at a time. With OCSP stapling, the server fetches a signed, recent status response and includes it in the handshake so the client need not ask.
Revocation checking has practical limits. Lists can be large, status servers can be slow or unreachable, and asking the CA about every site a person visits reveals their browsing. Many clients therefore soft-fail: if they cannot get a status, they continue. That makes the check easy to defeat for an attacker who can block it. Some browsers now distribute compact revocation data themselves instead of querying CAs, and the industry has moved toward shorter certificate lifetimes, so a compromised certificate stops working soon even if revocation is missed.
When verification is turned off
Faced with the third error above, a developer might reach for a setting that disables certificate verification. Every HTTP library has one:
# Avoid: each of these accepts any certificate from anyone
requests.get(url, verify=False) # Python
NODE_TLS_REJECT_UNAUTHORIZED=0 node report.js # Node.js
curl --insecure https://internal-reports.harborclinic.example/
The error goes away because the client no longer checks anything. The connection is still encrypted, but to whoever answered. Anyone who can intercept the traffic, on a shared network or through a compromised router, can present their own certificate and read or change everything, including credentials. Settings like this also tend to outlive the test that needed them and reach production.
The fix is to give the client the trust it is missing, and only that trust. For the internal service, the client can be configured to trust the clinic's private root for that connection:
requests.get(url, verify="/etc/harborclinic/internal-root-ca.pem")
For an expired or misnamed certificate, the fix belongs on the server: renew it or issue one with the right names. Issuing, renewing, and revoking certificates follows those steps from the server's side.