Python ships expedited security releases for 3.15, 3.14, and 3.13 to plug a broken certificate authority
A single CA produced private keys small enough to guess. Python's tooling was trusting it anyway.
The Python core team released three maintenance updates in one day: Python 3.15.1, Python 3.14.9, and Python 3.13.17. They aren't fixing ordinary bugs. The whole point is to slip a patched pip (26.2.2) into your hands, one that carries certifi 2026.10.19 with a freshly distrusted certificate authority. The CA in question, eMudhra (branded emSign for its web certs), had eight intermediate CA private keys compromised. Remove that trust, and Python's network tools stop accepting forged HTTPS connections from sites including python.org and pypi.org.
Hugo van Kemenade announced all three on the Python Insider blog. The release team also includes Thomas Wouters, Ned Deily, and Steve Dower.
The releases carry the labels "expedited security releases" for 3.15 and 3.14, and a "special security release" for 3.13. That one crossed into security-only mode recently. Oddly, the team normally doesn't bump pip inside older-branch security releases, for backward-compatibility reasons. They made an exception for 3.13 here because it had only just entered that mode, and shipping a special release with installers seemed prudent.
The releases, at a glance
The unifying fix is identical across all three installs. Your bundled pip goes to 26.2.2. certifi goes to 2026.10.19. Both exist to defuse the eMudhra/emSign incident.
The rest of the changelog varies. 3.15.1 is the first maintenance release of the 3.15 series and carries roughly 330 bugfixes, build tweaks, and doc changes from 117 contributors. 3.14.9 is the ninth maintenance release of the 3.14 branch with about 168 such changes from 74 contributors. 3.13.17 is the security-only update.
Each package ships the usual spread: macOS installers for macOS 10.15 and later on 3.14/3.15, Windows 32-bit, 64-bit, and ARM64 installers, gzipped and XZ source tarballs, embeddable packages, iOS and Android frameworks, plus Sigstore and SPDX SBOM signatures layered on top of SHA-256 checksums. The Windows installer increasingly comes through the Python Install Manager from the Microsoft Store, though the old-school installer sticks around for the full 3.14 and 3.15 cycles.
Here's a bonus Mac users will appreciate. All three installers bundle a pre-release of Tk that resolves a hang on macOS 27.0 for tkinter apps, most notably the IDLE shell (CPython issue #158053). So upgrading for security quietly kills a real usability bug at the same time.
A laughably weak key
This is where the story gets funny in the worst way. eMudhra disclosed on October 20 that eight of its intermediate CA keys were compromised. The report lives at emudhra.we-have-a-problem.com, which is at least thematically apt.
But the actual cause? A bad private key. Tiran (Iyazi Suleman), a Python core contributor, put it plainly in the Discourse thread.
"The 'security incident' is a bad private key. An elliptic key with private key value d=4 is not secure… All 8 eMudhra CAs had private keys with d < 256."
Elliptic-curve cryptography depends on private keys being ~256-bit random blobs. These were tiny, guessable numbers. d less than 256. In some cases d was just 4. Someone could compute the key in about four arithmetic operations.
The flawed key generation dates back to April 2025. There are no Certificate Transparency log entries for these intermediates in that window, though, which is a silver lining for browser users. CT-enforcing clients like Firefox, Chrome, Edge, and Safari couldn't have been hit through them. The trouble is that a huge number of non-browser clients ignore CT entirely, opening up a far broader attack surface.
The proof is embarrassingly simple. The original reporter, who signs off as "Wayne," signed a message using the private key of each of the eight intermediate CAs and handed it to the root program teams on October 17. The disclosure itself is a masterclass in understatement.
"We have a problem here," reads a message signed simply "Wayne."
proof.zip contains, for each intermediate, the certificate, its public key, and a signature verifiable with OpenSSL. Anyone can confirm the keys were truly exposed rather than merely suspected.
Keep in mind why this bites Python specifically. JacobCoffee laid out the problem in Discourse: pip always loads CA certificates from its vendored certifi in addition to the system trust store, and there's no supported way to turn that off. So any pip older than 26.2.2 keeps trusting the compromised intermediates no matter what your OS does.
Distrusting the bad root at the OS level sounds reasonable. It doesn't help. pip ships its own bundled copy of trusted certs via certifi and uses it alongside the system store. Old pip versions keep accepting forged certificates regardless. Upgrading pip is the only real fix. If you use uv, set UV_SYSTEM_CERTS=true to force it at the host trust store. For pip, no such shortcut exists.
But how do you download the pip upgrade safely when your current pip might be talking to an attacker mid-installation? sethmlarson of the Python security team has the answer. Install pip directly from its URL with a pinned SHA-256 checksum. Verify that checksum out-of-band, from python.org, PyPI, or this article. A MITM attacker who can forge certificates still can't serve a tampered wheel.
From that clean install you can bootstrap a secure copy of certifi and uv.
What you should do
The incident triggered near-universal patching across the ecosystem. curl published a CA bundle dated 2026-10-20. Go shipped 1.27.3 and 1.26.10. Node.js, Rust's rustls-webpki and webpki-roots, Deno, Electron, and Bun all got updates, as did every major Linux distro's ca-certificates package. Go blocked the compromised intermediates by SPKI hash, and Tiran filed a CPython ticket (#160040) proposing the same idea for Python's ssl module as defense-in-depth. Mozilla, Microsoft, and Apple already blocked the bad certs through their auto-updating revocation systems, but those blocks rolled out between October 17 and October 20, and many clients don't consume revocation data in real time. They need actual software updates.
So if you're keeping score, here's what matters:
Upgrade to one of these releases if you can. 3.15.1, 3.14.9, or 3.13.17 updates pip and certifi in one install, which is the cleanest path. On macOS it also clears the Tk/IDLE hang on 27.0.
For existing virtual environments, upgrade pip and uv individually, reinstalling pip from the pinned checksum URL above. If you're on a python.org macOS install that bundles certifi, upgrade both pip and certifi, since a framework copy won't pick up a system-wide change. And don't forget your container images: Docker official images and pinned lockfiles can stay vulnerable until refreshed. That point came up repeatedly in Discourse discussions about docker-library/python. Rebuild anything that pins an old pip.
Community members already built tools for the common "I have fifty venvs" problem. newbery shared a bash script that locates virtual environments under $HOME via fd or find, verifies the wheel checksum, and upgrades pip in each. Hugo van Kemenade posted a POSIX shell snippet that loops over multiple Python framework versions on macOS.
Riding on 3.15
These expedited releases land just weeks after Python 3.15.0 went stable on October 9, about a week behind schedule after last-minute lazy-import release blockers forced an extra release candidate (rc3 alone carried ~156 bugfixes from 82 contributors). For maintainers, this is also the moment Python 3.10 — released in 2021 — officially lost support, giving projects one more nudge to target 3.11 or newer.
3.15 is a big version. PEP 810 adds an explicit lazy import through a new lazy soft keyword, deferring module loading until the name is first used. PEP 814 introduces frozendict, an immutable built-in mapping that isn't a dict subclass. PEP 661 adds a sentinel builtin, PEP 798 allows unpacking in comprehensions, and PEP 686 makes UTF-8 the default encoding independent of locale.
The experimental JIT compiler got a real upgrade, reportedly a 7 to 8 percent geometric mean speedup on x86-64 Linux and 11 to 12 percent on AArch64 macOS. Independent benchmarking by Miguel Grinberg found 3.15 largely keeps pace with 3.14 in raw speed, with the JIT being the notable exception. PEP 799 brings a dedicated profiling package to the standard library, headlined by Tachyon, a statistical sampling profiler that attaches to running processes at rates up to 1,000,000 Hz. PEP 831 enables frame pointers by default, improving stack unwinding for profilers, debuggers, and eBPF tools.
Simon Willison framed the whole moment as "Hello Python 3.15, goodbye 3.10," a clean chance to drop an old version and finally lean on features from 3.11 like tomllib, asyncio.TaskGroup, and modern SQLite helpers.
The lesson runs deeper than one language. A single CA generating private keys small enough to guess effectively broke trust for an entire chain, a reminder that cryptographic hygiene at the root stays the weakest link in web security. Python keeps moving forward regardless, riding 3.15's lazy imports, built-in profiler, free-threaded stable ABI, and a real JIT speedup into the next stretch.
Head here the actual download links.
