Volver al blog
BunSeguridadTLS

eMudhra deja el SSL/TLS: qué cambia en tu app Bun

23 de octubre de 2026·7 min de lectura·Diego Horvatti

eMudhra anunció que va a salir del mercado global de certificados SSL/TLS. Si alguno de tus dominios usa un certificado emitido por ella, en algún momento va a expirar y no habrá renovación. Este post es para quien corre una app Bun en producción y quiere saber si le afecta, cómo averiguarlo y cómo migrar sin sustos.

Ya hablé aquí de la CA de eMudhra comprometida. Esta es la continuación natural de la historia: la empresa decidió salir del negocio. El comunicado oficial está en el sitio de eMudhra.

Qué anunció eMudhra

eMudhra es una autoridad certificadora india, conocida por la marca emSign. Vende certificados digitales de varios tipos, y el SSL/TLS público es solo una parte del negocio. Es la parte que nos importa: el certificado que hace aparecer el candado en el navegador y que tu fetch valida cuando llama a una API.

La noticia, resumida: la empresa va a dejar de ofrecer certificados SSL/TLS públicos en el mercado global. Sus clientes tendrán que migrar a otra CA.

En la práctica, salir de este mercado significa tres cosas:

  • Nada de certificados nuevos.
  • Nada de renovaciones.
  • Los certificados actuales siguen válidos hasta que venzan, salvo que algo fuerce una revocación antes.

El último punto es el que más pesa. Después de un incidente de seguridad, los root programs de los navegadores (Chrome, Mozilla, Apple, Microsoft) tienen poca paciencia. Si alguno deja de confiar en la raíz de la CA, el certificado sigue "válido" en el papel y el navegador lo rechaza igual.

Por qué importa aunque nunca le hayas comprado nada

Quizás estés pensando: "nunca compré un certificado de eMudhra, así que no va conmigo". Puede que no. Pero hay dos escenarios en los que sí va contigo.

El primero es el certificado que no compraste tú. Pudo ser el cliente, el equipo de infra de hace tres años o un revendedor. Muchas empresas compran certificados en un paquete de "seguridad" y nadie mira qué CA hay detrás.

El segundo es la API de terceros que llama tu backend. Si una pasarela de pago, un organismo público o un socio usa un certificado emSign y ese certificado deja de aceptarse, lo que se rompe es tu fetch. Y el error llega como UNABLE_TO_VERIFY_LEAF_SIGNATURE a las tres de la mañana, sin ninguna pista de que la causa es una CA saliendo del mercado.

El certificado es la única dependencia de tu proyecto que tiene fecha fijada para romperse.

Cómo decide Bun en quién confiar

Este detalle sorprende a mucha gente. Por defecto, Bun no lee la lista de CAs del sistema operativo. Trae su propio bundle de certificados raíz, basado en la lista de Mozilla, igual que Node.

Eso tiene dos consecuencias:

  1. Si Mozilla quita una raíz de la lista, una versión futura de Bun deja de confiar en ella. Solo te das cuenta cuando actualizas el runtime.
  2. Si tu empresa instaló una CA interna en el sistema, Bun la ignora por defecto.

Para el segundo caso hay dos caminos. Para añadir una CA extra sin tocar el código:

NODE_EXTRA_CA_CERTS=./certs/ca-interna.pem bun run start

O, en las versiones más recientes, pedirle a Bun que use la lista del sistema operativo:

bun --use-system-ca run start

Y para un caso puntual, pasas la CA directamente en el fetch:

const res = await fetch("https://api.socio.com/pedidos", {
  tls: { ca: Bun.file("./certs/socio-ca.pem") },
});

Ahora la opinión fuerte: nunca resuelvas un error de certificado con rejectUnauthorized: false. Ese "arreglo" desactiva toda la validación TLS y deja el camino abierto a un man-in-the-middle. Lo he visto en producción, commiteado con el comentario // temporal. Era de 2021.

Cómo saber si algún dominio tuyo usa emSign

Se puede hacer un inventario rápido. En la terminal, openssl ya lo resuelve:

echo | openssl s_client -connect api.tusitio.com:443 -servername api.tusitio.com 2>/dev/null \
  | openssl x509 -noout -issuer -enddate

Si el issuer contiene "emSign" o "eMudhra", anota el dominio y la fecha de expiración.

Si son muchos dominios, un script en Bun lo resuelve. Usa la API node:tls, que Bun implementa:

import tls from "node:tls";

const hosts = ["tusitio.com", "api.tusitio.com", "api.socio.com"];

for (const host of hosts) {
  const socket = tls.connect({ host, port: 443, servername: host }, () => {
    const cert = socket.getPeerCertificate();
    const issuer = `${cert.issuer?.O ?? ""} ${cert.issuer?.CN ?? ""}`;
    const flag = /emsign|emudhra/i.test(issuer) ? "  <-- ATENCIÓN" : "";
    console.log(`${host}: ${issuer} | expira ${cert.valid_to}${flag}`);
    socket.end();
  });
  socket.on("error", (err) => console.log(`${host}: error ${err.message}`));
}

Pon en la lista tus dominios y los de las APIs externas que llama el backend. La segunda parte es la que todo el mundo olvida.

También vale la pena revisar:

  • Los archivos .pem versionados en el repositorio (git grep -l "BEGIN CERTIFICATE").
  • Los secrets del CI y del proveedor de hosting.
  • Las apps móviles con certificate pinning. Es el peor caso: cambiar el certificado sin cambiar el pin tumba la app a quien no haya actualizado.

Cómo migrar sin tumbar producción

Si encontraste un certificado de eMudhra, la migración en sí es simple. Lo que requiere cuidado es el orden de los pasos.

1. Elige la nueva CA. Para la mayoría de los proyectos, Let's Encrypt vía ACME lo resuelve gratis. Si necesitas un certificado OV/EV por contrato o exigencia del cliente, entonces toca una CA comercial.

2. Revisa el registro CAA en el DNS. Si el dominio tiene un registro CAA que solo autoriza a eMudhra, la nueva CA se negará a emitir. Esto frena muchas migraciones:

dig CAA tusitio.com +short

Si solo aparece 0 issue "emsign.com", añade la nueva CA antes de pedir el certificado.

3. Emite e instala el nuevo certificado antes de quitar el viejo. En Bun.serve, el cambio es directo:

Bun.serve({
  port: 443,
  tls: {
    cert: Bun.file("/etc/ssl/nuevo/fullchain.pem"),
    key: Bun.file("/etc/ssl/nuevo/privkey.pem"),
  },
  fetch: (req) => new Response("ok"),
});

Usa el fullchain, no solo el certificado hoja. Sin los intermedios, el navegador a veces funciona porque completa la cadena por su cuenta, pero el cliente HTTP de tu socio falla. El clásico "en mi máquina funciona".

4. Si tienes pinning, publica el nuevo pin antes. Lanza una versión de la app que acepte los dos pins, el viejo y el nuevo, espera a que la base de usuarios actualice y solo entonces cambia el certificado.

5. Automatiza la renovación. Y esto vale con o sin eMudhra.

El problema real es renovar a mano

eMudhra es solo el detonante. El CA/Browser Forum aprobó la reducción gradual de la validez de los certificados públicos: 200 días a partir de marzo de 2026, 100 días en 2027 y 47 días en 2029. Quien todavía renueva certificados a mano, con un recordatorio en el calendario, va a sufrir mucho antes de 2029.

Así que usa la salida de eMudhra como excusa para hacer lo que ya debería estar hecho:

  • Certificados emitidos y renovados por ACME, sin intervención humana.
  • Una alerta cuando falten 14 días para que expire cualquier certificado, incluidos los de las APIs externas. El script de arriba, corriendo en un cron diario, ya cubre buena parte de esto.
  • Ninguna CA hardcodeada en un lugar que no controlas.

Si tu setup ya es así, la noticia de hoy se queda en una línea del changelog. Si no, se convierte en un incidente.

Mi opinión

La noticia en sí no asusta. Que una CA salga del mercado pasa, y el ecosistema TLS está diseñado para eso: hay muchas CAs, la validez es corta y existe un protocolo de automatización. Lo que asusta es la cantidad de equipos que ni saben qué CA firma sus propios certificados.

Corre el script, revisa el CAA y automatiza la renovación. Es una tarde de trabajo y te ahorra una madrugada en vela después. Si quieres ver cómo organizo la infra y el deploy en mis proyectos, echa un vistazo a los proyectos de este sitio.

Resumen para LinkedIn

eMudhra va a salir del mercado de SSL/TLS, y muchos equipos ni saben qué CA firma sus propios certificados.

Si el certificado de tu dominio es emSign, va a expirar y no habrá renovación. Peor aún si es la API de tu socio: ahí lo que se rompe es tu fetch, a las tres de la mañana.

En Bun, el primer paso es un script de 15 líneas que lista el emisor y la validez de cada dominio. Después toca revisar el registro CAA en el DNS y cambiar al fullchain de la nueva CA antes de quitar el certificado viejo.

Y por favor, nada de rejectUnauthorized: false con el comentario "// temporal".

Con la validez bajando a 47 días hasta 2029, quien renueva certificados a mano va a sufrir de todas formas. Escribí el paso a paso completo en el blog, el enlace está en los comentarios.

#Bun #TLS #DevOps #SeguridadInformática #JavaScript