Chiavi CA eMudhra: come sistemare Python, pip, uv e i trust store
Otto chiavi CA eMudhra erano così deboli da ricalcolarle. Cosa è vero, cosa è gonfiato e come aggiornare certifi, pip, uv e i trust store contro il MITM.
Pubblicato da Zero Hunt, un red team AI autonomo su un'appliance on-premise con AI privata: penetration test automatizzato per reti e infrastrutture, in black box o gray box, con una persona che approva ogni passo che conta.
Un thread virale di questa settimana ha presentato l'incidente eMudhra come "Python affronta un'emergenza di sicurezza enorme". Il titolo è esagerato; i fatti sotto non lo sono. Otto chiavi di certification authority intermedie sono state generate così male che chiunque può ricalcolare la chiave privata dal certificato pubblico — una valeva l'intero numero 4. Quelle intermedie si agganciano a radici che certifi di Python, pip, uv, Go, Node.js, curl e quasi tutti i trust store Linux distribuivano come fidate. I browser sono stati corretti in poche ore; la coda lunga sono gli strumenti che gli sviluppatori usano tutto il giorno, ed è la parte che vale la pena sistemare stasera.
Notizia in evoluzione — pubblicata alle 23:34 (21:34 UTC) del 22 ottobre 2026. Aggiornata man mano che i vendor pubblicano le correzioni.
Non è un bug di Python e non riguarda solo Python. È un problema di trust store, e Python è semplicemente uno dei posti più chiari dove vederlo e correggerlo.
Cosa è successo davvero
In una cerimonia di generazione delle chiavi dell'aprile 2025, eMudhra — CA indiana le cui radici erano (o sono) nei programmi di Mozilla, Microsoft, Apple e Chrome — ha prodotto un secondo set di chiavi private deboli per otto CA intermedie, accanto a quelle forti previste, e nei certificati sono finite le deboli. Il 17 ottobre 2026 un ricercatore ha riportato ai root program la prova firmata di controllo su tutte e otto le chiavi (sito dell'incidente eMudhra, curato da Dennis Jackson). The Next Web ha confermato il dettaglio della chiave privata pari a 4; Christian Heimes del team Python ha notato che tutte e otto avevano scalari privati sotto 256.
Perché conta: chi ricalcola una di queste chiavi può emettere un certificato valido per qualsiasi hostname — pypi.org, python.org, il tuo registro interno — e, se è sul percorso di rete, intercettare o riscrivere il traffico TLS verso ogni client che si fida ancora della catena. È lo scenario monster-in-the-middle descritto dal Python Security Response Team il 20 ottobre.
"Ho vibe-codato il proxy malevolo per circa 0,30 $… legge l'SNI, e se l'utente vuole parlare con PyPI intercetta la connessione, toglie gli hash dall'indice e ripacchettizza la wheel." — uno sviluppatore nel thread docker-library/python, 22 ottobre. L'attacco costa poco da costruire; la parte difficile per l'attaccante è solo finire sul cavo.
Cosa è vero e cosa i post virali gonfiano
| Affermazione in circolazione | Verdetto | Il fatto reale |
|---|---|---|
| 8 chiavi CA compromesse, una era il numero 4 | ✅ | Tutte e otto le intermedie avevano scalari privati sotto 256; una era 4 (eMudhra) |
| Si poteva impersonare python.org, PyPI e altri siti | ⚠️ | Vero in linea di principio, ma serve una posizione sul percorso (MITM) e un client che salta la Certificate Transparency; nessun abuso osservato |
| "I pip vecchi si fidano ancora dopo l'aggiornamento dell'OS" | ✅ | pip include la sua certifi e ignora lo store di sistema; pip < 26.2.2 continua a fidarsi (C. Heimes) |
| Le immagini Docker vanno ricostruite | ✅ | Le immagini ufficiali python:* hanno avuto pip 26.2.1 fino al 21 ott; i layer in cache tengono i bundle vecchi (docker-library/python#1142) |
| I browser sono ancora esposti | ❌ | Chrome/Firefox hanno bloccato il 17 ott, Microsoft il 20, Apple il 22, via CRLSet/OneCRL (Google) |
| Certificati già falsificati e usati | ❌ | Google non ne ha trovati nei log CT; eMudhra dice che l'intermedia in produzione ha emesso zero certificati (Google, comunicato eMudhra) |
Quindi: rischio reale, nessuno sfruttamento noto, e finestra stretta perché richiede che l'attaccante sia sul tuo percorso di rete. Proprio per questo il bersaglio realistico è un build server su una rete condivisa, un runner CI o un portatile sul Wi-Fi di un bar — non il tuo portatile a casa dietro un solo NAT.
Due cose tengono al sicuro i browser e i tuoi strumenti forse no. La prima è la Certificate Transparency: Chrome, Firefox e Safari rifiutano un certificato server mai registrato in un log CT pubblico, e nessuna di queste intermedie ne ha mai registrato uno. curl, Python, Go e quasi tutti i client a riga di comando non applicano la CT, ed ecco perché sono il bersaglio facile. La seconda è la revoca push — CRLSet, OneCRL, CRLite — che gli strumenti a riga di comando pure ignorano. Per tutto il resto la correzione è smettere di fidarsi della radice.
Remediation
Procedi in quest'ordine: trova ogni client che ha un proprio trust store, aggiorna quelli che ce l'hanno, ricostruisci ciò che è incorporato in un'immagine.
1. Sono colpito? Verifica senza tirare a indovinare
Su una stessa macchina coesistono tre trust store diversi, e aggiornarne uno non fa nulla per gli altri.
# A) il trust store di Python (requests, httpx e pip lo usano)
python -c "import certifi, sys; print(certifi.__version__, certifi.where())"
# < 2026.10.19 → vulnerabile
# B) la copia PROPRIA di pip (separata da quella sopra)
pip --version # pip < 26.2.2 porta la vecchia certifi
pip inspect 2>/dev/null | grep -i certifi
# C) le webpki roots incluse in uv
uv --version # uv < 0.13.4 è vulnerabile
# D) il trust store di sistema
# Debian/Ubuntu:
dpkg -l ca-certificates | tail -1
# RHEL/Fedora:
rpm -q ca-certificates
Il test autorevole e dal vivo è il checker sul sito di divulgazione. Una connessione che riesce significa che il client si fida ancora di un'intermedia compromessa:
# se restituisce una pagina invece di un errore di certificato, quel client è ancora vulnerabile
curl https://dv-g3b-emudhra.we-have-a-problem.com/
# un secondo host prova il percorso con la cross-firma Sectigo:
curl https://dv-g3b-aaa-emudhra.we-have-a-problem.com/
# e questo verifica se il client salta anche la Certificate Transparency:
curl https://dv-g3b-noct-emudhra.we-have-a-problem.com/
Per cercare offline le due radici in un bundle CA specifico:
grep -E "emSign ECC Root CA - G3|AAA Certificate Services" /percorso/cacert.pem
# emSign ECC Root CA - G3 SHA-256: 86A1ECBA089C4A8D3BBE2734C612BA341D813E043CF9E8A862CD5C57A36BBE6B
# AAA Certificate Services SHA-256: D7A7A0FB5D7E2731D771E9484EBCDEF71D5F0C3E0A2948782BC83EE0EA699EF4
Attenzione alla seconda radice: sei delle otto intermedie sono cross-firmate dalla "AAA Certificate Services" di Sectigo, fidata da anni e presente nei bundle vecchi. Togliere solo la radice emSign non basta; va eliminato anche il percorso della cross-firma AAA.
2. Patch — versioni corrette esatte
Verificate sui metadati di rilascio di ogni progetto il 22 ottobre 2026:
| Strumento | Versione corretta | Nota |
|---|---|---|
| certifi | 2026.10.19 | la 2026.10.18 toglieva tutte le radici emSign; la .19 ha ripristinato quelle non colpite |
| pip | 26.2.2 | include certifi 2026.10.19 |
| uv | 0.13.4 | rimuove la radice dalle sue webpki-roots (uv ignora lo store di sistema per default) |
| Python (installer) | 3.15.1, 3.14.9, 3.13.17 | includono pip 26.2.2; 3.12, 3.11, 3.10 non ricevono l'aggiornamento di pip |
| Node.js | 26.12.1, 24.22.1, 22.23.4 | |
| Go | 1.27.3, 1.26.10 | aggiunge una denylist SPKI (CVE-2026-111943) |
| conda / pixi | 26.9.4 / 0.82.1 | |
| sigstore | 4.5.1 |
La trappola del bootstrap: non puoi fidarti di un semplice pip install --upgrade pip su una macchina vulnerabile, perché il download stesso potrebbe essere intercettato. L'istruzione del PSRT è fissare la wheel con l'hash:
python -m pip install \
"pip @ https://files.pythonhosted.org/packages/3f/06/3e4420302363fbb71be3c7f35ce042f54bfca330e3e66b3a2e3c4cca579f/pip-26.2.2-py3-none-any.whl#sha256=fd7f4dfa8190e014013b9f08f3e973f4e4c26ffd179a204895533a7254813a70"
# poi, con il pip ora fidato:
python -m pip install --upgrade certifi
Per uv, fai il bootstrap dallo store di sistema che macOS e Windows hanno già corretto: UV_SYSTEM_CERTS=true uv self update. Su Linux funziona una volta aggiornato il pacchetto ca-certificates della distribuzione.
3. Correggi il trust store dell'OS
Aggiorna il pacchetto della distribuzione — è ciò che leggono il pip patchato di Debian, curl e quasi tutti gli strumenti:
# Debian / Ubuntu
apt update && apt install --only-upgrade ca-certificates
# RHEL / Fedora / Alma / Rocky / Amazon Linux 2023
dnf upgrade ca-certificates
# Alpine
apk upgrade ca-certificates
# macOS: l'OS le blocca, ma curl/git/python3 integrati leggono /etc/ssl/cert.pem,
# che NON si aggiorna da solo — vedi il sito di divulgazione per l'one-liner awk che toglie le due radici.
Tra le versioni corrette di ca-certificates: Debian 20250419+deb13u1, Ubuntu 20260816ubuntu0.24.04.1, Alpine 20261020-r0, Fedora 2026.2.92_v9.0.319 (sito di divulgazione). Se la tua distribuzione non ne ha ancora pubblicata una, lo stesso sito elenca i comandi update-ca-certificates / trust extract di blocklist per distribuzione — usali come tampone, non come configurazione permanente.
4. Ricostruisci i container e svuota le cache
Un host patchato non fa nulla per un'immagine costruita la settimana scorsa.
# ricostruisci SENZA cache dei layer così prende i nuovi ca-certificates/pip/certifi
docker build --no-cache -t myapp:fixed .
- Scarica immagini base fresche: le immagini ufficiali
python:*sono state aggiornate dopo il 21 ottobre (3.13–3.15 seguono CPython; le immagini 3.11/3.12 tengono il pip vecchio — aggiungiPIP_CERT=/etc/ssl/certs/ca-certificates.crto fissa pip 26.2.2 per hash nel Dockerfile). - Invalida le cache delle dipendenze in CI (
actions/cachedi GitHub Actions, cache di GitLab, cache dei layer Docker) che tengono una wheelcertifio uncacert.pemobsoleti. - Cerca le copie incorporate che nessun gestore di pacchetti tocca:
find / -name "cacert.pem" 2>/dev/null # bundle dentro le cartelle delle app
find / -path "*certifi/cacert.pem" 2>/dev/null
Ognuna che contiene ancora emSign ECC Root CA - G3 richiede l'aggiornamento del pacchetto che la possiede o la sostituzione del file.
5. Verifica e previeni
Rilancia il checker curl contro ogni classe di client che hai corretto; ora un errore di certificato significa che sei pulito. Poi irrigidisci contro la prossima CA sbagliata:
- Fissa con gli hash.
pip install --require-hashese i lockfile (uv, Poetry, pip-tools) trasformano una wheel sostituita in un errore netto: è la vera difesa quando il trasporto è in dubbio. - Preferisci il trust store dell'OS dove puoi (
truststoreper Python,UV_SYSTEM_CERTSper uv), così un solo aggiornamento dica-certificatescopre lo strumento invece di N bundle incorporati. - Limita l'uscita di rete in CI e usa un mirror privato con hash verificati, così un runner di build non può essere reindirizzato all'indice dell'attaccante.
- Ricostruisci le immagini a cadenza, non solo quando qualcosa si rompe — un refresh mensile delle immagini base avrebbe chiuso tutto in pochi giorni senza correre.
Cosa non si sa ancora
eMudhra dichiara di non avere prove che le chiavi deboli siano state recuperate o usate prima della divulgazione, e che l'unica intermedia in produzione abbia emesso zero certificati (comunicato eMudhra). Poiché i client che saltano la CT non lasciano traccia pubblica, la risposta onesta per gli strumenti non-browser è che l'assenza di prove non dimostra che nessuno abbia usato un certificato falsificato contro un client curl/pip su una rete ostile — ed è l'intero motivo per aggiornare ora invece di aspettare la rimozione della radice da Chrome il 27 ottobre. La denylist delle intermedie nel modulo ssl di CPython (issue #160042) era ancora in revisione al momento della scrittura; le correzioni per singolo strumento qui sopra non ne dipendono.
Dove si inserisce Zero Hunt
Il buco che questo incidente scopre non è "esiste una patch" — esiste — ma "quale dei miei host si fida ancora di una CA che non dovrebbe, e un attaccante sulla mia rete potrebbe davvero sfruttarlo". È una domanda di validazione, ed è quella a cui Zero Hunt risponde. Come red team AI autonomo su appliance on-premise, enumera i client TLS e i trust store su un segmento e, a un livello di autonomia approvato dall'operatore, dimostra se un host sulla LAN può essere messo in posizione di intercettazione — il passo on-path di cui questo attacco ha bisogno — poi ripete il test dopo che hai aggiornato i bundle per confermare che il client ora rifiuta la catena falsificata. Ogni azione gira con lo human in the loop e nulla lascia il perimetro, il che conta quando ciò che stai testando è la fiducia stessa dei tuoi certificati. Non sostituisce l'aggiornamento di certifi e ca-certificates: ti dice, con prova, se la patch è davvero completa su tutte le macchine che avevi dimenticato di avere.
Per la vicenda di supply chain della stessa settimana, vedi la nostra nota sulla release manomessa LiteLLM 1.107.0 su PyPI e il tracker degli incidenti di Black October. Per come dare priorità quando ogni avviso dice "critico", vedi le tier basate sul rischio della BOD 26-04 di CISA.
È sfruttabile nel tuo ambiente?
Zero Hunt lo verifica sulla tua rete: un red team AI autonomo su un'appliance on-premise, con AI privata, in black box o gray box e con una persona che approva ogni passo che conta. La prova di cosa è sfruttabile, la correzione e le evidenze firmate — nessun dato esce dal tuo perimetro.