Ubuntu retira una raíz de eMudhra; WordPress 7.1.3, aún no
Ver imagen del artículo

«Your client successfully connected to the site. That’s bad!». Es lo que contesta el sitio de pruebas del incidente de eMudhra a un cliente que todavía acepta sus claves comprometidas. Me lo devolvió la API HTTP de WordPress 7.1.3, la versión que se descarga hoy de wordpress.org.
Ubuntu retiró la raíz afectada el 20 de octubre. WordPress no verifica con ese almacén, o no solo con él: lleva su propia lista en wp-includes/certificates/ca-bundle.crt, y la de 7.1.3 es de julio. Cuento qué ha pasado, qué arregla Ubuntu, qué queda fuera y cómo hacer que WordPress use la lista del sistema.

Lo Probado es del 21 de octubre de 2026, de 06:19 a 06:50 UTC, con paquetes desempaquetados sin instalar, PHP 8.3 y wp-cli. No he cambiado el almacén de ninguna máquina. Los avisos y el código citado van como Revisado (así los distingo).
Qué le ha pasado a eMudhra
eMudhra es una autoridad de certificación india. El 17 de octubre a las 20:06 UTC, Mozilla recibió, reenviado por Chrome, el aviso de Wayne, alguien de la comunidad: ocho intermedios de eMudhra tenían las claves privadas comprometidas, seis de TLS y dos de firma de código. Lo cuenta el bug 2083449, público desde el día 20.
Según Dennis Jackson, de Mozilla, en ese bug, son claves P-384 generadas el 30 de abril de 2025, y sus claves privadas eran números menores que 256: «effectively all-zeroes apart from the last byte». Mozilla las recuperó por su cuenta a las 21:48 UTC del mismo día 17.
Del aviso al paquete
2026-10-17
Bloqueo en Firefox
A las 22:59 UTC Mozilla ya había publicado el bloqueo en OneCRL para Firefox, según su blog.
2026-10-18
Revocados
eMudhra revoca los ocho entre las 09:24 y las 09:38 UTC. La CRL de emSign Root TLS CA – G3, que he bajado, trae las horas de los seis de TLS.
2026-10-19
Sectigo y las distribuciones
Sectigo revoca sus certificados cruzados y Mozilla avisa a quienes mantienen almacenes (el correo, en oss-security).
2026-10-20
Fuera la raíz
Mozilla retira de urgencia emSign ECC Root CA – G3 (Chrome se compromete a lo mismo), sale NSS 3.131.1 y Ubuntu publica USN-8944-1.
2026-10-21
WordPress, en trunk
A las 03:15 UTC entra en trunk la lista nueva (r64671, ticket #66355).
Las ocho firmas de prueba que publica el sitio de pruebas dan Verified OK con la clave pública de su certificado; lo he comprobado. Según eMudhra, en una primera estimación, bajo esa raíz tiene unos 30.000 certificados de clientes que migrar, cerca del 97 % llegados por ACME.
Esos certificados cuelgan de otros intermedios de la misma raíz. Mozilla dice en su aviso del 19 que eMudhra no usó los ocho comprometidos para firmar a clientes y que Certificate Transparency no muestra certificados emitidos con ellos, lo que no descarta certificados sin registrar para quien no exige CT.
[WARN] Los ocho están revocados desde el 18, y sus cruzados de Sectigo, desde el 19. Eso protege a Firefox y a Chrome, que reciben sus propias listas de bloqueo (OneCRL, CRLSets).
curl y PHP no consultan la revocación por defecto (en las pruebas de abajo entran sin pestañear), y Python y Go tampoco, según su documentación: para ellos solo cuenta qué raíces hay en el almacén que usan.
Revocado no quiere decir rechazado
La diferencia se ve con openssl verify sobre el intermedio EM DV TLS CA – G3B, con el certificado cruzado que lo une a la raíz, la CRL de eMudhra y el almacén de Ubuntu anterior al arreglo:
# Almacén aislado: -no-CApath y -no-CAstore para que no se cuele /etc/ssl/certs
openssl verify -no-CApath -no-CAstore -CAfile ubuntu-20260601.pem \
-untrusted cruzado.pem em-dv-tls-ca-g3b.pem
# em-dv-tls-ca-g3b.pem: OK
# Lo mismo, pidiéndole que mire la CRL (lastUpdate Oct 18 09:38:46 2026 GMT)
openssl verify -no-CApath -no-CAstore -crl_check -CRLfile roottlscag3.pem \
-CAfile ubuntu-20260601.pem -untrusted cruzado.pem em-dv-tls-ca-g3b.pem
# error 23 at 0 depth lookup: certificate revoked
OpenSSL sabe que está revocado, pero solo si se lo preguntas y le das la lista. Una trampa al repetirlo: sin -no-CApath, openssl verify -CAfile también consulta /etc/ssl/certs, y el resultado pasa a depender de la máquina.
Lo que arregla el paquete de Ubuntu
USN-8944-1 trae ca-certificates 20260816ubuntu0.24.04.1 para Ubuntu 24.04, y equivalentes para 22.04, 26.04 y 26.10. Según Launchpad, entró en noble-security a las 15:54 UTC del 20. Su registro de cambios nombra una sola raíz «due to intermediate cert compromise»: emSign ECC Root CA – G3.
# Los dos paquetes, desempaquetados sin instalar
dpkg-deb -x ca-certificates_20260601~24.04.1_all.deb antes
dpkg-deb -x ca-certificates_20260816ubuntu0.24.04.1_all.deb despues
diff <(ls antes/usr/share/ca-certificates/mozilla) \
<(ls despues/usr/share/ca-certificates/mozilla) | grep '^[<>]'
# < Atos_TrustedRoot_2011.crt
# < emSign_ECC_Root_CA_-_G3.crt
# < Entrust_Root_Certification_Authority.crt
# < ePKI_Root_Certification_Authority.crt
# > SECOM_TLS_ECC_Root_CA_2024.crt
# > SECOM_TLS_RSA_Root_CA_2024.crt
# < SecureSign_Root_CA12.crt
# > Telia_EC_TLS_Root_CA_v3.crt
# > Telia_RSA_TLS_Root_CA_v3.crt
Salen esa raíz y otras cuatro por el paso a la lista 2.90 de Mozilla, y entran cuatro. Siguen las otras tres de emSign: NSS 3.131.1 quitó también C1 y ECC C3, pero a petición de eMudhra y en otro expediente (bug 2083709), y Mozilla no retira, por ahora, emSign Root CA – G1.
Con las doce cadenas del sitio de pruebas, el paquete viejo acepta las seis que suben por esa raíz y el nuevo las rechaza. Las otras seis van por el cruzado de Sectigo y fallan con los dos: Ubuntu 24.04 ya había quitado Comodo AAA Services root con el paquete de junio.
# La versión instalada: en 24.04, 20260816ubuntu0.24.04.1 o posterior
dpkg-query -W ca-certificates
# La raíz ya no debe estar entre los enlaces del almacén
ls /etc/ssl/certs | grep -i 'emSign_ECC_Root_CA_-_G3' || echo "no está"
# La prueba de verdad: tiene que fallar con el error 60
curl -sS https://dv-g3b-emudhra.we-have-a-problem.com/
# Si no lo tienes, no esperes a unattended-upgrades
sudo apt update && sudo apt install --only-upgrade ca-certificates
Por qué no esperar a unattended-upgrades
Lo he leído en apt.systemd.daily (apt 2.8.3) y en los temporizadores por defecto de 24.04:
- Las listas se refrescan como mucho una vez por día de calendario, y
apt-daily, que las baja, sale a las 6:00 y a las 18:00 con hasta 12 horas al azar. apt-daily-upgrade, que instala, sale a las 6:00 con hasta una hora al azar y usa las listas que haya.- Si las del día 20 se bajaron antes de las 15:54 UTC, el paquete puede esperar a la mañana del 22. Es una cuenta, no una medida.
WordPress lleva su propia lista
La API HTTP de WordPress, la de wp_remote_get(), que usan las actualizaciones y la comprobación de versiones, no pide a PHP el almacén del sistema. En class-wp-http.php de 7.1.3, sslcertificates vale por defecto su ca-bundle.crt, y Requests se lo pasa a curl como CURLOPT_CAINFO.
# La lista de WordPress 7.1.3, tal cual sale del tar.gz de wordpress.org
head -4 wordpress/wp-includes/certificates/ca-bundle.crt | tail -1
# ## Certificate data from Mozilla as of: Thu Jul 16 03:12:01 2026 GMT
grep -c 'emSign ECC Root CA - G3' wordpress/wp-includes/certificates/ca-bundle.crt
# 1
# Qué usa libcurl cuando la aplicación no dice nada (PHP 8.3)
php -r '$c = curl_init(); var_dump(curl_getinfo($c, CURLINFO_CAINFO), curl_getinfo($c, CURLINFO_CAPATH));'
# string(34) "/etc/ssl/certs/ca-certificates.crt"
# string(14) "/etc/ssl/certs"
La trampa: WordPress cambia el fichero, no el directorio. La libcurl de Ubuntu sigue mirando /etc/ssl/certs, y basta con que la raíz esté en uno de los dos. Con la lista nueva de trunk y un --capath que solo contiene la raíz retirada, curl entra en el sitio de pruebas: HTTP 200. Arreglar solo WordPress tampoco basta.
En un WordPress 7.1.3 real, con wp-cli y un CApath vacío (aislado de la máquina), wp_remote_get() devuelve HTTP 200 y la frase del principio: le basta su propia lista. Trunk ya lo corrige con r64671; a las 06:57 UTC del 21, la rama 7.1 aún no, y no había salido ninguna 7.1.4.
Por qué importa: las actualizaciones
Pretend this error didn’t happen.
Comentario declass-wp-upgrader.phpcuando falla la firma de un paquete, WordPress 7.1.3
WordPress comprueba la firma de los paquetes de wordpress.org, pero si no cuadra sigue adelante: wp_signature_softfail vale true por defecto. Lo muestra como un aviso más, y si la firma no aparece, ni eso (salvo con WP_DEBUG). Mi lectura: hoy es la conexión TLS lo que separa a un WordPress de un paquete cambiado por el camino.
Un mu-plugin para que use el almacén del sistema
No toco wp-includes: lo machaca la siguiente actualización. Propongo un filtro en https_ssl_verify que cambie la lista de WordPress por la del sistema, la que Ubuntu mantiene con sus paquetes. Va en wp-content/mu-plugins/ y se deshace borrando el fichero.
<?php
/**
* Plugin Name: CA del sistema para la API HTTP
* Description: Verifica TLS con el almacén del sistema en vez del ca-bundle.crt de WordPress.
* Version: 1.1
*/
add_filter(
'https_ssl_verify',
function ( $verify ) {
// false es que alguien ha desactivado la verificación a propósito: no lo tapo.
if ( false === $verify ) {
return $verify;
}
// Ruta de Debian y Ubuntu; en otra distribución, la constante en wp-config.php.
$sistema = defined( 'DAV_CA_SISTEMA' ) ? DAV_CA_SISTEMA : '/etc/ssl/certs/ca-certificates.crt';
// Con open_basedir, is_readable() dice false aunque curl sí pueda leerlo:
// en ese caso me fío de la ruta. Sin él, si no está, mejor la lista de WordPress.
if ( ini_get( 'open_basedir' ) || is_readable( $sistema ) ) {
return $sistema;
}
return $verify;
},
99
);
La primera versión comprobaba is_readable() antes de cambiar la ruta, por prudencia. Me equivoqué: con open_basedir activo, is_readable() da false para todo lo que queda fuera de las rutas permitidas, y el plugin volvía en silencio a la lista de WordPress. La prueba seguía dando HTTP 200.
curl, en cambio, no pasa por open_basedir: curl_setopt() con CURLOPT_CAINFO fuera de esas rutas devuelve true y el fichero se lee. La versión 1.1 se fía de la ruta cuando hay open_basedir, y con ella la misma prueba da el error 60 que se busca.
[WARN] El riesgo de la 1.1 es el contrario: con open_basedir y una ruta que no existe, todo HTTPS de WordPress falla con «cURL error 77: error setting certificate file», actualizaciones incluidas. Lo he provocado con una ruta inventada. Fuera de Debian y Ubuntu, define antes DAV_CA_SISTEMA.
Comprobación y vuelta atrás
La comprobación es la misma petición que hace WordPress, lanzada con wp-cli. Con el plugin y el paquete nuevo de Ubuntu tiene que salir el error 60. Si sale la frase del principio, uno de los dos (el fichero que usa WordPress o /etc/ssl/certs) conserva la raíz.
# Desde la raíz de WordPress; solo lee
wp eval '$r = wp_remote_get("https://dv-g3b-emudhra.we-have-a-problem.com/");
echo is_wp_error($r) ? $r->get_error_message() : wp_remote_retrieve_body($r), "\n";'
# Bien: cURL error 60: SSL certificate problem: unable to get local issuer certificate
# Mal: Your client successfully connected to the site. That's bad!
# Vuelta atrás: WordPress vuelve a su lista en la petición siguiente
rm wp-content/mu-plugins/ca-del-sistema.php
El paquete de Ubuntu no tiene vuelta atrás razonable. Si un servicio deja de conectar porque su certificado colgaba de esa raíz, eMudhra anuncia en el bug que pasará a sus clientes a su raíz G1. Volver a meter la raíz retirada no es la salida.
Otros almacenes que no pasan por apt
WordPress no es el único con copia propia. Lo comprobado bajando paquetes, y lo leído:
- certifi, de Python: la 2026.10.18 quitó las cuatro raíces de emSign y la 2026.10.19 devolvió tres y dejó fuera ECC G3. El aviso GHSA-6q48-6c7w-9gw2 da por corregida la 2026.10.18, y en las dos ruedas he comprobado que ECC G3 no está.
- pip trae dentro su propia copia de certifi: la de la 26.2.1 tiene la raíz y la de la 26.2.2, del día 20, no.
- El
cacert.pemde curl del 20 de octubre ya no la trae, ni composer/ca-bundle 1.6.0 y 1.4.16, del mismo día. - Go 1.27.3 y 1.26.10 (CVE-2026-111943) descartan en
Verifylas cadenas con esos intermedios: su anuncio no los nombra, pero el commit lleva las huellas SPKI de los ocho. Hay que recompilar cada programa; esto lo he leído, no compilado.
# Listas de CA con la raíz retirada en el sistema de ficheros raíz; solo lee
sudo find / -xdev -type f \( -name 'cacert*.pem' -o -name 'ca-bundle.crt' \
-o -name 'ca-certificates.crt' \) -exec grep -l 'emSign ECC Root CA - G3' {} + 2>/dev/null
Solo encuentra las listas que llevan el nombre de cada raíz en texto, como la de WordPress o la de certifi. La que genera Ubuntu en /etc/ssl/certs es PEM pelado, sin nombres: para esa está la prueba con curl.
Qué no he probado
- Node.js, Java y las imágenes de contenedores, que llevan el almacén del día en que se construyeron.
- Los paquetes de Ubuntu 22.04, 26.04 y 26.10: solo he desempaquetado el de 24.04.
- El plugin en un multisitio o con un transporte que no sea curl.
- Windows y macOS, donde además cuenta el cruzado de Sectigo: su raíz AAA Certificate Services sigue en los almacenes de Microsoft y Apple, según la propia Sectigo en el bug.
- Recuperar las claves. Ni lo he intentado ni lo voy a hacer en una entrada.
En la guía de las cuatro libcurl conté librerías, quién las cargaba y quién las actualizaba. No conté almacenes de certificados, y era la otra mitad de la pregunta: una libcurl del sistema bien parcheada sirve de poco si la aplicación le pasa su propia lista.
El resto de lo que he escrito sobre WordPress está en desarrollo web y WordPress, y lo de Ubuntu, en las guías de Ubuntu y Linux.
[INFO] Pendiente: anotar aquí la versión de WordPress que lleve la lista nueva a la rama 7.1. A las 06:57 UTC del 21 de octubre solo estaba en trunk.
$ tail -f comentarios.log
La conversación empieza aquí
Escribir un comentario ↓[esperando] 0 mensajes en el hilo
Nadie ha escrito todavía. Si lo has probado en tu máquina, cuenta qué te salió: otra versión u otro resultado también ayudan.