Skip to content
Putting technology to work.
Insights to guide decisions and action.

Search articles

certifi removes emSign: checking the bundles in requests and pip

Table of contents · 6 items

If you use requests in Python batch jobs that call APIs from internal core systems, or in web application containers, whether a TLS endpoint is trusted is in many cases decided by a certificate bundle (cacert.pem) contained in a small package called certifi. Updating the OS certificates does not change this bundle. So when a problem is found in the bundle, you keep using the old bundle unless you rebuild your Python dependencies and container images.

On October 20, 2026 (UTC), the certifi advisory GHSA-6q48-6c7w-9gw2 was registered in the GitHub Advisory Database as CRITICAL. It states that, for the root certificate emSign ECC Root CA - G3 included in the bundle, the private key of an eMudhra intermediate CA chaining to it was compromised and the CA revoked it. The fixed version is certifi 2026.10.19.

For this article, we reviewed the advisory, the certifi repository, and the published artifacts of PyPI and pip, and compared the contents of each version's bundle in a local virtual environment. For developers who maintain Python business systems and containers, we explain what changed, where old bundles remain, and how to check.

The only root removed is emSign ECC Root CA - G3

The advisory's description is as follows (excerpt).

Certifi versions before 2026.10.18 include the emSign ECC Root CA - G3 root certificate in their bundled CA store. Eight eMudhra intermediate CA certificates that chain to this root have compromised private keys and the CA has revoked them.

It states that an attacker holding the compromised key could issue certificates that appear valid for any hostname, and if positioned on the network path, could intercept or tamper with the TLS traffic of clients that trust the certifi bundle. Examples of affected software listed include requests, urllib3, pip and the Python.org installer for macOS. The affected range is 2019.03.09 or later and below 2026.10.19.

The fix came in two stages. Below are the results of examining the certifi repository history and extracting and comparing cacert.pem from each version.

Version (PyPI release date and time, UTC)Number of certificates in the bundleemSign roots
2026.7.22 (July 22)1214: G1, G3, C1 and C3
2026.10.18 (October 18, 16:21)117None (all 4 removed in "Remove emsign")
2026.10.19 (October 20, 02:24)1203: G1, C1 and C3 (G3 not restored)

The advisory states, "Certifi 2026.10.18 removed all emSign roots from the bundle. Certifi 2026.10.19 restored the emSign roots that are not affected. Upgrade to 2026.10.19 or later." Comparing the 2026.7.22 and 2026.10.19 bundles certificate by certificate, the only difference was that emSign ECC Root CA - G3 (SHA-256 fingerprint 86:A1:EC:BA:…:6B:BE:6B) was gone.

Diagram showing how emSign root certificates are handled in each certifi version. 2026.7.22 contains four: emSign Root CA - G1, emSign ECC Root CA - G3, emSign Root CA - C1 and emSign ECC Root CA - C3. In 2026.10.18 all four were removed. In 2026.10.19, G1, C1 and C3 were restored, and only G3, which chains to the compromised intermediate CA, remains removed. Upgrade to 2026.10.19 or later

2026.10.18 also removed the three unaffected roots. In the editorial team's assessment, environments upgraded to 2026.10.18 may fail verification if they connect to endpoints using certificates that chain to these three roots. If you are upgrading now, do not stop at 2026.10.18; go to 2026.10.19 or later.

Which library trusts which bundle

To identify what needs updating, consider separately whether each library you use reads the certifi bundle or the OS certificate store. Here is what we confirmed in the source.

UsageTrusted certificatesEvidence checked
requests without specifying verifycertifi's cacert.pemrequests/certs.py uses from certifi import where, and adapters.py uses DEFAULT_CA_BUNDLE_PATH
pip (Python 3.10 or later)OS store + certifi bundled with pipindex_command.py additionally loads certifi.where() into the truststore context
Using urllib3 on its own without passing ca_certsOS storeutil/ssl_.py calls load_default_certs()

The comment in requests/certs.py says, "This module returns the preferred default CA certificate bundle. There is only one — the one from the certifi package." httpx also has certifi as a PyPI dependency (we did not check how it loads the contents this time).

Since 24.2, pip verifies with both the OS certificates and certifi on Python 3.10 or later (NEWS: "Use system certificates and certifi certificates to verify HTTPS connections on Python 3.10+."). Even with settings that use the OS store, the certifi bundle bundled with pip continues to be loaded. The NEWS for pip 26.2.2 (released October 20) says "Upgrade certifi to 2026.10.19," so the bundled certifi has been upgraded to the fixed version. The bundled certifi in the previous release, 26.2.1, is 2026.06.17 and includes G3.

As the table above shows, urllib3, which the advisory lists as an example, uses the OS store when used on its own without passing ca_certs. However, if the application passes ca_certs=certifi.where(), it uses the certifi bundle, so check what is specified in the code.

We compared the bundles of each version ourselves

On October 21, 2026, we created a virtual environment in the editorial team's test environment (Linux, Python 3.11.17) and installed wheels obtained from PyPI (with hashes checked against PyPI's values) to verify.

$ python -m venv venv
$ venv/bin/python -m pip --version
pip 24.0 from …/venv/lib/python3.11/site-packages/pip (python 3.11)
$ venv/bin/python -c "import pip._vendor.certifi as c; print(c.__version__)"
2023.07.22

# certifi 2026.7.22 と requests 2.34.2 を入れた状態
certifi 2026.07.22
requests DEFAULT_CA_BUNDLE_PATH = …/site-packages/certifi/cacert.pem
certs 121 | emSign ECC Root CA - G3: True | emSign Root CA - G1: True

# certifi 2026.10.19 に上げた状態
certifi 2026.10.19
certs 120 | emSign ECC Root CA - G3: False | emSign Root CA - G1: True

# pip を 26.2.2 に上げた状態
pip 26.2.2 vendored certifi 2026.10.19

The default bundle of requests pointed to cacert.pem of the certifi installed in the virtual environment, and upgrading certifi removed G3 from the bundle.

Another finding was that the pip in a virtual environment created with python -m venv is old. In this environment, pip 24.0 bundled with Python 3.11 was installed, and its bundled certifi was 2023.07.22. Even if you upgrade the application's certifi, pip itself keeps using this old bundle. If your Dockerfile does not upgrade pip after python -m venv, upgrade pip as well.

If you set REQUESTS_CA_BUNDLE, requests uses that bundle instead of certifi (we confirmed with REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt that verify becomes that path). In that case, keeping the specified bundle up to date is the responsibility of whoever manages it.

Steps to check containers and production environments

From here on are the editorial team's suggestions. Old bundles can remain in three places: application dependencies, container images, and pip.

  1. Search your lock files. Check the certifi version in requirements*.txt, poetry.lock, uv.lock and Pipfile.lock. Even if it is not a direct dependency, it is installed as a dependency of packages such as requests.
  2. Check the version in running environments. Run python -c "import certifi; print(certifi.__version__, certifi.where())" in the image or on the server and confirm it is 2026.10.19 or later. On servers with multiple virtual environments, run it with the Python the application actually uses.
  3. Check the versions bundled with pip and uv. Use python -c "import pip._vendor.certifi as c; print(c.__version__)" to confirm it is 2026.10.19 or later (pip 26.2.2 or later). pip 26.2.2 targets Python 3.10 or later, and as of October 21, no fixed pip release usable with Python 3.9 or earlier has been published on PyPI. uv uses its own bundled certificate bundle rather than certifi, so use uv --version to confirm it is at or above 0.13.4 (released October 20), the fixed version for a separate advisory, GHSA-hq85-5cpj-mr62.
  4. Rebuild images. The advisory asks you to "rebuild container images that vendor certifi." If certifi is pinned in a lock file, raise the version before building. Also check that old layers are not being reused from the base image cache.
  5. Search for verify settings. Search for verify=, ca_certs=, REQUESTS_CA_BUNDLE and SSL_CERT_FILE, and wherever a bundle other than certifi is referenced, confirm with that bundle's maintainer that it has been updated.

Even if you cannot rebuild images right away, certifi is a package with few dependencies, and the Python versions it supports on PyPI are 3.7 or later. requests requires certifi>=2023.5.7, so upgrading only certifi to 2026.10.19 still satisfies requests's dependency requirement. The editorial team recommends first upgrading certifi and pip, then checking items 2 and 3 above with the rebuilt image.

In Python installed from Linux distribution packages, as noted in the comment in requests/certs.py, where() may be replaced with the OS bundle. In that case, you need to update the OS's ca-certificates or similar. The page compiled by the person who reported this issue (emudhra.we-have-a-problem.com) lists fixed versions for Ubuntu, Debian, Alpine and others, but we have not checked each distribution's own announcements this time.

Reviewing certificate operations as a whole is also covered in protecting staging on the assumption that it will be discovered through CT logs and shorter SSL certificate validity periods and automatic renewal. Other changes in the uv 0.13 series are covered in what changed in uv 0.13.

Scope of verification

On October 21, 2026, we reviewed the JSON for GHSA-6q48-6c7w-9gw2 in github/advisory-database, the certifi/python-certifi repository (the commits "Remove emsign" and "Restore unaffected emSign roots," and cacert.pem at tags 2026.07.22, 2026.10.18 and 2026.10.19), the public information on PyPI for certifi, pip, requests and httpx, and the pypa/pip repository (NEWS.rst, index_command.py) (document research). We also compared the certificates in the certifi 2026.7.22, 2026.10.18 and 2026.10.19 bundles with openssl in a virtual environment on Linux (Python 3.11.17), and checked the bundle path used by requests, how REQUESTS_CA_BUNDLE takes effect, and the versions of certifi bundled with pip 24.0, 26.2.1 and 26.2.2 (hands-on testing). We have not checked traffic using the compromised intermediate CA's certificate, connection failures with 2026.10.18, Windows or macOS, packages in each Linux distribution, or how the Mozilla certificate store handles this. On October 21, before publication, we also checked the guidance for Python users on discuss.python.org (asking users to update to certifi 2026.10.19, pip 26.2.2, uv 0.13.4 and others and to rebuild images), the uv advisory GHSA-hq85-5cpj-mr62, and the list of intermediate CAs on the page compiled by the reporter.

For updating the dependencies and containers of business systems and batch jobs built with Python, please contact us through development, AI and automation consultations.

References

Share this articleXFacebook
Kakeru Suzuki

Fascinated by the possibilities of technology, has had a deep interest in programming and digital art since student days

Turn this article's theme into your company's next step

Concrete steps forward for your organization.

We organize your desired architecture, legacy systems, and operational requirements to formulate your next steps toward execution.

  • Desired architecture
  • Integration with existing environments
  • Operational requirements
Consult on development & operations initiatives

You can consult with us from the initial conceptual stage. Details from this article will be carried over to the inquiry form.

Receive the latest articles by email