Security Guides

eMudhra (emSign) intermediate CA keys compromised: what Python users need to do about pip, certifi and uv

Eight eMudhra intermediate CA keys were weak enough for anyone to recover. Python projects ask users to move to pip 26.2.2, certifi 2026.10.19 and uv 0.13.4. Who is affected, the safe upgrade order, Docker, lockfiles, OS CA bundles, and how to confirm you are done.

Published 2026-10-24 Updated 2026-10-24 Last verified 2026-10-24 21 min read
On this page

Who this is for: anyone who installs packages with pip, uv, conda or pixi, anyone running programs that make HTTPS calls with libraries such as requests, and anyone maintaining Python Docker images or CI.

This article is based on official statements from the Python projects (CPython and pip developers), the certifi, uv and conda projects, Mozilla, Google, eMudhra and the Linux distributions. It does not cover how to attack anything or how the keys were recovered. Social media has been calling this a "Python security emergency", but no code execution flaw has been found in Python itself.

続報待ち:この記事は公式発表に合わせて更新します

October 24, 2026 (JST)

まだ分かっていないこと

  • Whether CPython's ssl module will reject the compromised intermediate keys (the change is in review and not merged yet)
  • How Python 3.11 and 3.12, and their Docker official images, will handle the old pip (the release managers plan to backport the ssl module change that blocks the compromised intermediates instead of bumping pip; not merged as of October 24)
  • eMudhra's final root-cause report
  • Chrome's root removal, scheduled for October 27 (Pacific time)

The Python projects published their notice on October 20 and released Python 3.15.1, 3.14.9 and 3.13.17 on October 21. Chrome and Firefox blocked the affected intermediates on October 17. No exploitation has been announced. We will add the decisions on 3.11 and 3.12 and any further CPython changes once they are confirmed.

Am I affected? A quick table by environment

Whether you are affected depends on which part of your Python setup carries a CA list. One machine can hold separate lists for each Python version, each way Python was installed and each virtual environment.

EnvironmentWhat carries the old listWhat to do
Any environment that uses pipThe certifi vendored inside pip (survives an OS fix)Upgrade pip to 26.2.2+
Programs configured to use standalone certifi, such as requestsThe standalone certifi packageUpgrade certifi to 2026.10.19+
Environments using uvThe CA list built into uvUpgrade uv to 0.13.4+
conda, Mamba, pixiEach tool plus the certifi and ca-certificates packagesUpgrade to conda 26.9.4+ and pixi 0.82.1+, and upgrade certifi and ca-certificates in the environment
Python from the python.org macOS installerThe certifi shipped with the installerFollow the official post to upgrade pip and certifi; on 3.13+ move to the fixed Python
Docker images and CIWhatever pip, certifi and OS bundle existed at build timeRebuild without the cache
pip or certifi installed from Linux packages (apt, dnf)The OS ca-certificates (Debian patches pip to use it)Update ca-certificates to the fixed version

When using urllib3 directly, the default is the system CA store. Update that store; if your configuration explicitly uses certifi or another bundle, update that bundle too (urllib3's certificate verification guide).

What happened (from official statements)

The eMudhra intermediate key incident (October 2026)
Report
On October 17, 2026, a security researcher reported to the Apple, Google, Microsoft and Mozilla root programs and to eMudhra that the private keys of eight eMudhra intermediate certificates could be recovered
Scope
Eight intermediates. Six are for TLS (HTTPS) and were trusted by Chrome, Mozilla, Microsoft and Apple; two are for code signing. All were created in a key generation ceremony on April 30, 2025. The six TLS intermediates chain to the root emSign ECC Root CA - G3 (the two code-signing ones sit under a different root)
Cause
Google says the private keys were generated with insufficient randomness. Mozilla says it recovered the keys independently and confirmed that the key generation was flawed. eMudhra says a step in the ceremony produced a second, weak set of keys alongside the strong ones, and the weak keys were the ones used. The full root cause is still under investigation
What the keys allow
Mozilla says the keys could be used to issue fraudulent HTTPS certificates for sites the holder does not control. An attacker who can intercept a user's connection could use them to impersonate websites and potentially read or modify traffic
eMudhra's response
Revoked all eight on October 18. It says seven were never used in production and the one that was had not issued any certificate. It stopped issuance under this root on October 20, and on October 22 announced that it will exit the global SSL/TLS certificate business
Browsers
Chrome blocked the keys through CRLSet on October 17. Firefox blocked them the same day through OneCRL and shipped Firefox 158.0.1 and ESR updates on October 20. On October 20 Mozilla decided on an emergency removal of the root and removed it from NSS 3.131.1. Chrome will remove it on October 27 (Pacific time)
Python projects
They published a notice on October 20 (updated through October 22) asking users to upgrade to at least certifi 2026.10.19, pip 26.2.2, uv 0.13.4, sigstore 4.5.1, conda 26.9.4 and pixi 0.82.1, and to rebuild container images with layer caching disabled. On October 21 they released Python 3.15.1, 3.14.9 and 3.13.17, which bundle pip 26.2.2

certifi is the widely used Python package that holds a list of trusted certificate authorities. It is derived from Mozilla's list, and requests and pip use it to decide whether a server's certificate is genuine. certifi 2026.10.18 (published October 18, UTC) removed all emSign roots, and 2026.10.19 (published October 20, UTC) restored the emSign roots that are not affected. certifi's own advisory lists 2026.10.18 as patched, but the GitHub Advisory Database entry and the Python projects recommend 2026.10.19 or later for compatibility with the unaffected emSign roots. certifi 2026.10.23 then removed two more emSign roots to follow Mozilla (eMudhra says that removal was planned earlier and is unrelated to this incident).

How serious is it?

Chrome, Firefox

Blocked on October 17. Also reject certificates missing from public CT logs

pip before 26.2.2

Always reads its vendored certifi, even after an OS fix

requests and other certifi-based clients

Use standalone certifi older than 2026.10.19

uv before 0.13.4, Docker images

Keep the list from build time

Browsers were protected by pushed blocks. Python tools trust forged certificates until their own CA lists are updated.

Browsers are protected by two things: they receive certificate blocks automatically, and they reject certificates that are not in the public Certificate Transparency (CT) logs. Google says no TLS certificates chaining to the affected intermediates were logged to CT before its block, so no forged certificate could have been used against CT-enforcing clients.

pip and requests use neither mechanism. They judge only by the CA list they carry, so if that list is old, they accept a forged certificate as genuine.

What could happen

  • An attacker on the network path redirects connections to PyPI, python.org and other sites to a fake server
  • pip install installs a tampered package
  • API keys or passwords sent with requests are read

What an attacker needs, and what we know

  • The attacker must be able to sit on your network path (the same Wi-Fi, a compromised router, a corporate proxy and so on)
  • There is no code execution flaw in Python itself
  • No exploitation had been announced as of October 24

Do not be complacent. Mozilla said on October 20 that multiple people had independently recovered the keys and some had disclosed this publicly, so making a forged certificate is not hard. Avoid running an old pip anywhere someone could get on the path, such as public Wi-Fi, shared networks or the network around a CI runner. Our guide to public Wi-Fi risks covers that side.

First, check your versions

1

Check the pip version

Run this with the Python you want to check (inside the virtual environment, if you use one). Version 26.2.2 or later is fixed.

python -m pip --version

Running pip on its own can point at another Python's pip. Use python -m pip so you know which Python you are checking.

2

Check the certifi version

This shows the standalone certifi that requests and similar libraries use. Version 2026.10.19 or later is fixed. If it is not installed, pip prints a "not found" warning.

python -m pip show certifi

The certifi vendored inside pip is a separate copy. Run python -m pip debug and look for the certifi== line under "vendored library versions". After you upgrade to pip 26.2.2, it reads 2026.10.19.

3

Check uv and conda

Run uv --version (0.13.4+ is fixed), conda --version (26.9.4+) and pixi --version (0.82.1+).

4

List every place Python is installed

One machine often has the OS Python, a python.org Python, Pythons from Homebrew or pyenv, and per-project virtual environments (such as .venv). A virtual environment is any directory with a pyvenv.cfg file. Writing the full list down first makes it much harder to miss one.

Upgrade in a safe order

The order matters. An old pip trusts the affected certificates, so if it simply downloads a new pip and that download is intercepted, a fake pip could be installed. The Python projects recommend installing pip with a pinned file hash (a fingerprint of the file's contents) and then using that pip for everything else.

1

Upgrade pip to 26.2.2 with a pinned hash

The official post and the Python blog give a command with the pip 26.2.2 file URL and its SHA-256 hash. It has this shape. Copy the URL and hash exactly from the official post (we deliberately do not reproduce them here).

python -m pip install "pip @ <URL from the official post>#sha256=<hash from the official post>"

With a pinned hash, pip refuses to install a file whose contents differ, so an intercepted download cannot install a fake pip. The official post says browsers should already have updated their stores and can be used for checking. For example, open the pip 26.2.2 files page on PyPI and confirm the hash shown there matches.

2

Use the new pip to upgrade certifi and uv

Once pip is 26.2.2, use it to upgrade certifi. If you installed uv with pip, upgrade it here too.

python -m pip install --upgrade certifi
python -m pip install --upgrade uv

If you installed uv with its standalone installer, follow uv's advisory and move to 0.13.4 or later. The advisory says that if you switch uv to the system certificates instead, you must confirm the OS has already removed the affected root or intermediates.

3

Repeat in every virtual environment

pip and certifi live inside each virtual environment. The Python projects ask users to upgrade pip and uv in every existing virtual environment on the system. For old environments you no longer use, recreating or deleting them can be quicker.

4

Move 3.13 to 3.15 to the fixed releases; on 3.11 and 3.12, upgrade pip in every new venv

Python 3.15.1, 3.14.9 and 3.13.17 bundle pip 26.2.2, so new environments created with python -m venv also get the fixed pip.

The Python projects say there will be no 3.11 or 3.12 release that bundles a newer pip, because of compatibility risk. Upgrade pip first thing every time you create a virtual environment. pip 26.2.2 supports Python 3.10 and later. On 3.9 and older the fixed pip is not available, so upgrade Python itself (3.10 has also reached end of life).

5

For the python.org macOS Python, upgrade with that interpreter

Python from the python.org macOS installer ships certifi. The blog lists commands for 3.13, 3.14 and 3.15 (the official post shows 3.15 as an example) that call that interpreter directly, for example /Library/Frameworks/Python.framework/Versions/3.15/bin/python3.15, to upgrade pip and certifi. Upgrade the free-threaded build (such as python3.15t) separately. If pip install --upgrade certifi fails on the certifi shipped by the installer, the official post suggests reinstalling it with --ignore-installed --no-deps certifi.

Docker, CI and lockfiles

This is where fixes are most often missed. Fixing your laptop does nothing for images and CI runners, which keep the old list from build time.

1

Rebuild Docker images without the cache

The Python projects ask you to rebuild container images with layer caching disabled so the latest OS certificates are fetched. Re-pull the base image as well, for example with docker build --pull --no-cache. After rebuilding, run python -m pip --version inside the container to confirm.

On October 21 the Docker official images updated their 3.13 and later definitions to the fixed Python releases (3.15.1, 3.14.9 and 3.13.17). The image maintainers say they will not bump pip in the 3.11 and 3.12 tags unless Python publishes new releases. If you use 3.11 or 3.12 images, upgrade pip to 26.2.2 in your Dockerfile with a pinned hash.

2

Raise certifi pins in requirements and lockfiles

A pin such as certifi==2025.x.x in requirements.txt or constraints.txt puts the old certifi straight back. The same goes for uv.lock, poetry.lock, pylock.toml and Pipfile.lock. Raise certifi to 2026.10.19 or later, regenerate the lockfile and roll it out to your images and CI.

3

Exempt certifi and pip from dependency cooldowns

If you use a setting that waits a fixed period before accepting newly published versions (a dependency cooldown), the fix will not arrive until the period ends. A Python packaging maintainer warned that cooldown users stay exposed until they update. Exempt certifi, pip and uv from that setting.

4

Upgrade the actions and tools your CI uses

The pypa/gh-action-pypi-publish GitHub Action used for publishing to PyPI has a fixed release, v1.14.3. For cibuildwheel, upgrade to v4.3.4 or later (v4.3.3 brought the fix; v4.3.4 also covers pip on Python 3.9 and other targets). Check that your CI configuration does not pin older versions.

Watch for copies cross-signed by another CA

Some of the affected intermediates also had copies cross-signed by another certificate authority. Cross-signing lets the same intermediate key be trusted under a different root. A CPython core developer says the root used for that cross-signing is present in certifi releases before April 2025 and in old pip versions that bundle them (for example, pip 25.0.1 with certifi 2024.8.30). According to the cross-signing CA, that root had already been removed from the Chrome and Mozilla stores but was still trusted by Microsoft and Apple as of October 18 (Mozilla record). Debian, and Ubuntu 14.04 to 20.04 (USN-8944-2), also removed that cross-signing root alongside the emSign root. The cross-signing CA says it revoked the cross-signed copies on October 19, but most clients outside the browser do not check revocation. Deleting only the emSign root from an old bundle is not enough, because the cross-signing root would remain. Move to certifi 2026.10.19 or later (certifi releases since April 2025 no longer contain the cross-signing root).

Update the OS certificates (ca-certificates)

On Linux, the ca-certificates package holds the system CA list. curl and Pythons installed from OS packages use it.

DistributionOfficial noticeFixed version
Debian 13 (trixie)DSA-6572-1 (October 20)ca-certificates 20250419+deb13u1
Debian 12 (bookworm)DLA-4845-1ca-certificates 20250419~deb12u2
Ubuntu 26.10, 26.04, 24.04, 22.04USN-8944-1 (October 20)ca-certificates 20260816ubuntu0.(release)
Ubuntu 20.04, 18.04, 16.04, 14.04USN-8944-2 (October 20)Provided through Ubuntu Pro (ESM)

Debian's fix removes emSign ECC Root CA - G3 and the cross-signing root. Ubuntu's USN-8944-1 removes emSign ECC Root CA - G3 and, on 22.04 to 26.04, refreshes the list to version 2.90 of the Mozilla bundle; USN-8944-2 removes both roots on 14.04 to 20.04. pip installed from Debian's own package (python3-pip) is patched to read the OS list instead of the vendored certifi, so updating ca-certificates fixes it. On Windows and macOS, install the OS updates.

How to confirm you are done

1

Check three versions everywhere

On your machine, in every virtual environment, inside every Docker image and in CI, confirm pip 26.2.2+, certifi 2026.10.19+ and uv 0.13.4+. If even one old copy remains, you are not done.

2

Confirm the affected root is gone from certifi's list

python -m certifi prints the path of certifi's bundle file. If the name "emSign ECC Root CA - G3" no longer appears in it, the list is fixed.

grep -c "emSign ECC Root CA - G3" "$(python -m certifi)"

An output of 0 means it is gone. With Debian or Ubuntu's packaged certifi the file has no certificate names, so 0 proves nothing; check the ca-certificates version instead. pip's vendored list is fixed once pip is 26.2.2 or later.

3

Check pins and the OS

Search your requirements files and lockfiles for certifi and make sure nothing older than 2026.10.19 is pinned. On Linux, confirm ca-certificates is at or above the fixed version in the table above, for example with apt policy ca-certificates.

This site's view: protecting browsers does not protect everything else

Browser users were protected within hours without doing anything, because browsers receive blocks automatically and reject certificates missing from CT logs. pip, requests and CI images have neither. Each holds its own copy of the CA list, and the owner has to update every copy one by one. There are as many copies as there are virtual environments and images.

One habit also helps against the next incident of this kind: keep package hashes in your requirements (--require-hashes). If the hashes live in your own files, an intercepted download cannot swap in a package with different contents. See auditing dependencies with OSV-Scanner for pinning and checking dependencies, and the LiteLLM 1.107.0 malware incident for a case where the distribution channel itself was attacked.

For website operators

Chrome will stop trusting emSign ECC Root CA - G3 on October 27 (Pacific time). Google asks operators whose certificates chain to this root to get a new TLS certificate from another hierarchy (under a different root) in the Chrome Root Store immediately. If you use an eMudhra certificate, check the issuer's guidance.

Timeline

  1. April 30, 2025

    eMudhra creates intermediates, including the eight affected ones, in a key generation ceremony.
  2. October 17, 2026

    A security researcher reports that the eight keys can be recovered. Chrome (CRLSet) and Firefox (OneCRL) block them the same day.
  3. October 18

    eMudhra revokes the eight intermediates. certifi 2026.10.18 removes all emSign roots.
  4. October 19

    The cross-signing CA revokes its cross-signed copies.
  5. October 20

    Mozilla decides on emergency removal of the root. certifi 2026.10.19, pip 26.2.2 and uv 0.13.4 are released and the Python projects publish their notice. Debian and Ubuntu update ca-certificates. Google announces Chrome will remove the root on October 27.
  6. October 21

    Python 3.15.1, 3.14.9 and 3.13.17 and conda 26.9.4 are released. The Docker official images update their 3.13+ definitions to the fixed Python releases.
  7. October 22

    eMudhra announces it will exit the global SSL/TLS certificate business.
  8. October 23

    certifi 2026.10.23 removes two more emSign roots to follow Mozilla.

Sources (public record)

The facts in this article come from the public sources below. We do not reproduce how the keys were recovered or anything usable for an attack.

  • Python projects, "Information for Python users regarding eMudhra / emSign CA certificate security incident" (October 20, 2026; later posts checked through October 24) — discuss.python.org
  • Python, "Python 3.15.1, 3.14.9 and 3.13.17 are out" (October 21, 2026) — blog.python.org
  • certifi security advisory (GHSA-6q48-6c7w-9gw2) — github.com / GitHub Advisory Database github.com
  • certifi, "Remove emsign" (#437, October 18, 2026) — github.com / certifi release history pypi.org
  • pip changelog (26.2.2: upgrade certifi to 2026.10.19) — pip.pypa.io
  • uv security advisory (GHSA-hq85-5cpj-mr62) and 0.13.4 — github.com
  • conda, "Security updates for conda, Mamba, and Pixi" (October 21, 2026) — conda.org
  • Google, "Protecting Chrome and Android Users from Compromised eMudhra Intermediate CA Certificates" (October 20, 2026) — blog.google
  • Mozilla, "Protecting Firefox users following the compromise of eMudhra intermediate certificates" (October 20, 2026) — blog.mozilla.org
  • Mozilla incident record (Bug 2083449: the eight intermediates, revocation times, root removal, cross-signed copies) — bugzilla.mozilla.org
  • eMudhra, "eMudhra Statement on Recent Certificate Security Incident" (first published October 20, updated October 22) — emudhra.com
  • eMudhra, "eMudhra to exit global SSL/TLS certificate business" (October 22, 2026) — emudhra.com
  • Reporting on eMudhra's exit (The Record, October 22, 2026) — therecord.media
  • Debian, "DSA-6572-1 ca-certificates" (October 20, 2026) — lists.debian.org / "DLA-4845-1 ca-certificates" lists.debian.org
  • Ubuntu, "USN-8944-1" and "USN-8944-2" ca-certificates (October 20, 2026) — ubuntu.com / ubuntu.com
  • Docker official image (python) issue, "These images ship vulnerable pip" (fix for 3.13+, handling of 3.11 and 3.12) — github.com
  • CPython, "Distrust compromised eMudhra intermediate CA keys" (additional ssl module defense, not merged as of October 24) — github.com
  • Status of Python versions — devguide.python.org

Update history

2026-10-24: First version, based on the Python projects' October 20 notice and posts through October 23, the Python 3.15.1, 3.14.9 and 3.13.17 releases, the certifi, pip and uv advisories, statements from Google, Mozilla and eMudhra, and the Debian and Ubuntu ca-certificates updates. We will update this page once the handling of 3.11 and 3.12 and any further CPython change are decided.

Latest breaking reports

All breaking reports →

FAQ

QWhat happened?
A

On October 17, 2026, it was reported that the private keys of eight intermediate certificates from the Indian certificate authority eMudhra (six for TLS, two for code signing) could be recovered almost instantly because of a flaw in how they were generated in April 2025. According to Mozilla and Google, those keys let anyone create certificates for any website that look genuine to clients relying on an outdated CA list. eMudhra revoked all eight on October 18, and on October 20 Mozilla decided on an emergency removal of trust in the parent root certificate.

QIs this a vulnerability in Python itself?
A

No. It is not a flaw that lets anyone run code in CPython. The problem is that the CA lists Python's TLS clients trust (certifi, and the copy of certifi bundled inside pip) contained the affected root. An attacker on the network path who uses a forged certificate could redirect pip install or requests traffic to a fake server. If a download from PyPI is intercepted, a tampered package could be installed, which is why the Python projects ask users to update quickly.

QIf I update my operating system's certificates, is pip safe?
A

No. According to a CPython core developer, pip always loads the certificates from its vendored certifi in addition to the system trust store, and there is no supported way to turn that off. Any pip older than 26.2.2 keeps trusting the compromised intermediates even after the OS distrusts them, so pip itself must be upgraded. One exception: Debian's python3-pip package is patched to read the OS bundle instead of the vendored certifi, so updating ca-certificates fixes it.

QI use Python 3.11 or 3.12. What should I do?
A

The Python projects say there will be no 3.11 or 3.12 release that bundles a newer pip. ensurepip (the pip that python -m venv installs) stays old, so upgrade pip to 26.2.2 or later every time you create a virtual environment. The Docker official images for 3.11 and 3.12 also still ship the old pip. A change that makes CPython's ssl module reject the compromised intermediate keys is being prepared, but it had not been merged as of October 24.

QHas this been exploited?
A

No exploitation had been announced as of October 24. Google says no TLS certificates chaining to the affected intermediates were logged to Certificate Transparency before its block. eMudhra says it is not aware of any evidence that the weak keys were recovered or exploited before disclosure. Mozilla, however, said on October 20 that multiple people had independently recovered the keys and some had disclosed this publicly, so un-updated clients outside the browser can still be targeted.

QHow do I know I am done?
A

On the Python side you are done when every environment (your machine, each virtual environment, each Docker image, CI) has pip 26.2.2 or later, certifi 2026.10.19 or later and uv 0.13.4 or later. Also check that no requirements file or lockfile pins an older certifi, and on Linux that ca-certificates is at the fixed version. The section "How to confirm you are done" below lists the checks.