· via dev.to (home feed)
Python and Node still trust eMudhra's compromised CA intermediates after browser blocks
Eight eMudhra intermediate CAs with recoverable private keys were blocked by major browsers in mid-October, but Python requests and Node 22 fetch still accept TLS chains anchored to them, a developer test shows.

Browsers and server runtimes have split over eight compromised eMudhra intermediate certificate authorities. Firefox and Chrome distrusted the intermediates on 17 October, the day they were reported; Apple followed on the 19th and Microsoft on the 20th. Yet on the morning of 21 October, according to a dev.to post by Neeraj Sharma, Python's requests library and Node 22's built-in fetch() both completed requests against a public test host serving a certificate issued from one of the compromised intermediates, each returning a 200 response. The browser, in other words, is protected; the backend may not be.
What was compromised
The disclosure site for the incident lists eight intermediate CA certificates created by eMudhra in April 2025. Six issue TLS certificates — domain-, organization- and extended-validation variants across two generations, G3B and G3C — and two issue code-signing certificates. Each intermediate was also cross-signed by Sectigo's AAA Certificate Services root, so the incident spans 16 certificates sharing eight keys.
The flaw sits in key generation: the post reports that the private keys can be recovered from the public certificates, and the reporter demonstrated this by producing a valid signature with each of the eight keys. Anyone holding a recovered key can issue certificates under these CAs.
One mitigating fact exists. The disclosure says there are no Certificate Transparency log entries for these intermediates since April 2025, and concludes that browsers which enforce CT could not have been attacked. That safeguard reaches browsers only.
Why runtimes still trust them
Two trust chains are in play. In the first, a leaf certificate chains through a compromised intermediate to emSign Root TLS CA - G3, itself cross-signed by emSign ECC Root CA - G3 — a root shipped by Apple, Chrome, Microsoft and Mozilla. In the second, the chain runs through the AAA cross-sign to the AAA Certificate Services root, which Apple and Microsoft still trust even though Chrome and Mozilla have blocked that path.
Browsers cover both cases through intermediate-distrust mechanisms: Firefox's OneCRL and CRLite, Chrome's CRLSets, and equivalent blocks pushed by Apple and Microsoft. Per the post, Python, Node, Go and most SDKs do none of this. They load a static bundle of roots, usually skip revocation checking entirely, and do not enforce CT. If an anchoring root is present in the bundle, a compromised intermediate chains to it and the handshake succeeds.
A 151-line audit
The post ships a script built on the Python standard library plus the openssl CLI, with four subcommands:
- stores locates CA bundles on the machine — system paths and certifi — and flags any that contain one of the four anchoring roots, matched by the SHA-256 fingerprint of the DER certificate.
- client performs a live TLS handshake against two public test hosts, once with Python's default context and once with certifi's bundle; a successful handshake proves that runtime trusts a compromised intermediate. The author notes that requests, httpx, boto3 and most SDKs validate against certifi rather than the OS store.
- hosts pulls the chain served by your own endpoints and flags any certificate whose public key matches one of the eight compromised keys, matching on the SubjectPublicKeyInfo hash so a reissued certificate reusing a bad key is caught as well.
- verify runs an offline openssl verification of twelve chains captured from the disclosure site against any bundle you name.
The script exits with code 1 when a check fails, so it can gate a CI job.
Why it matters
This is a structural gap in the TLS ecosystem, not a bug in any single library. Browser vendors layer defenses — revocation lists, CT enforcement — while server-side runtimes rely on static trust bundles with neither. A backend, scheduled job or integration client built on Python, Node, Go or an SDK on top of them can therefore validate a certificate forged from the recovered keys, because the anchoring roots remain in common bundles. Until those anchors are removed or the intermediates are distrusted at the bundle level, the practical response is detection: audit local bundles and served chains the way the script does, and wire the check into CI so the gap is visible before someone exploits it.
- #tls
- #pki
- #python
- #node-js
- #security