Hackeo de US$ 5.600 millones en Binance: la lección de seguridad en Bun
Una billetera apareció en Arkham con la etiqueta "Binance-hacker-1029" y una cifra que asusta: US$ 5.600 millones atribuidos a un hackeo de Binance. Abrí la página de la entidad en Arkham Explorer y me quedé un rato mirando ese número. Este texto no va a hacerse el experto en blockchain. Quiero hablar de lo que este tipo de casos le enseña a quien escribe backend, y en mi caso eso significa Bun.
Lo que se sabe del hackeo de Binance hasta ahora
Voy a ser honesto contigo. Lo único concreto es una etiqueta pública en un explorador on-chain. Arkham agrupa direcciones en "entidades" y les pone nombre. En este caso, el nombre dice que es un hacker y que el dinero salió de Binance.
Todavía no vi un informe técnico confirmado que explique cómo pasó. Tampoco sé si los US$ 5.600 millones son el valor al momento del robo o el valor actual de los activos. En el mundo cripto esa diferencia suele ser enorme. Así que toma la cifra como aproximada hasta que salga un post-mortem oficial.
Aun así, se puede sacar una lección. La mayoría de los grandes robos a exchanges cae en pocas categorías:
- clave privada de una hot wallet filtrada o mal guardada;
- computadora de un empleado comprometida;
- dependencia maliciosa en la cadena de build;
- falta de alertas ante salidas anormales de fondos.
Ninguna exige un zero-day genial. Casi siempre el atacante solo aprovecha una puerta que quedó abierta.
Por qué debería importarle a un dev de Bun
Quizás estés pensando: "yo no tengo un exchange, hago una API de turnos". Justo. Pero el patrón de ataque es el mismo, y lo único que cambia es el tamaño del daño.
Tu proyecto tiene una clave de Stripe. Tiene un token de acceso a la base de datos. Tiene una service account de AWS o de Vercel. Si alguien se queda con eso, el daño ocurre en minutos, exactamente igual.
Y JavaScript tiene un problema histórico acá. Cualquier paquete de npm puede ejecutar código en tu máquina durante la instalación. En los últimos años hubo paquetes populares secuestrados justamente para robar variables de entorno y claves de billeteras.
El atacante no revienta la caja fuerte. Busca la llave que alguien olvidó en la puerta.
Lo que Bun ya hace por ti en la instalación
Acá Bun tiene una ventaja real, y la uso como argumento cuando me preguntan por qué migré.
bun install no ejecuta los scripts de ciclo de vida (postinstall y compañía) de las dependencias por defecto. Solo los ejecuta para los paquetes que habilitaste explícitamente en trustedDependencies dentro del package.json:
{
"trustedDependencies": ["esbuild", "sharp"]
}
Eso cierra la puerta más usada en ataques de supply chain en el ecosistema JS. Un paquete comprometido en lo más profundo de tu árbol no puede ejecutar nada solo por haber sido instalado.
Dos cosas que vale la pena agregar hoy al CI:
bun install --frozen-lockfile
bun audit
--frozen-lockfile hace que el build falle si el lockfile no coincide con el package.json. Nadie cambia una versión de dependencia a escondidas. bun audit revisa tus paquetes contra la base de vulnerabilidades conocidas.
No es una bala de plata. Un paquete malicioso todavía puede atacar cuando se importa en runtime. Pero sacas del juego el caso más común y más barato para el atacante.
Cómo guardar secretos fuera del .env
Este es el punto que más me molesta en proyectos ajenos. El .env se vuelve un baúl con todo adentro, queda en el disco en texto plano y un día alguien lo commitea sin querer.
Para desarrollo local, Bun 1.3 trajo Bun.secrets, que usa el almacén de credenciales del sistema operativo (Keychain en macOS, libsecret en Linux, Credential Manager en Windows):
import { secrets } from "bun";
await secrets.set({
service: "mi-app",
name: "stripe-key",
value: "sk_test_...",
});
const stripeKey = await secrets.get({
service: "mi-app",
name: "stripe-key",
});
En la práctica, la clave sale del archivo que cualquier script puede leer con cat. Para producción, sigue usando el gestor de la plataforma (variables de Vercel, Secrets Manager de AWS, Vault). La regla es simple: un secreto de verdad no vive en el repositorio ni en un archivo suelto.
Tres hábitos más que cuestan poco:
- una clave por entorno y por servicio, nunca la misma para todo;
- permisos mínimos: el token del worker de e-mail no necesita escribir en la base de datos;
- rotación con fecha fija en el calendario, no "cuando se pueda".
Monitorear lo que sale importa tanto como proteger lo que entra
Fíjate en algo. En la blockchain, todo el mundo vio el dinero salir. Por eso Arkham pudo etiquetar la billetera. El problema es que verlo después no devuelve nada.
En tu sistema vale el mismo razonamiento. Necesitas una alarma que se dispare cuando algo se sale de lo normal: muchos reembolsos, muchas exportaciones de datos, un usuario descargando la base entera a las tres de la mañana.
Una alerta simple en Bun cubre lo básico. Algo así, corriendo como cron:
const LIMITE_POR_HORA = 50_000; // en centavos, ajústalo a tu negocio
const res = await fetch(`${Bun.env.API_INTERNA}/retiros?ultima_hora=1`, {
headers: { Authorization: `Bearer ${Bun.env.TOKEN_LECTURA}` },
});
const retiros: { valor: number }[] = await res.json();
const total = retiros.reduce((suma, r) => suma + r.valor, 0);
if (total > LIMITE_POR_HORA) {
await fetch(Bun.env.SLACK_WEBHOOK!, {
method: "POST",
body: JSON.stringify({ text: `Salida anormal: ${total / 100} en la última hora` }),
});
}
Detalle importante: el token de este script es solo de lectura. Si comprometen el monitor, el atacante puede ver los números y nada más. Y una alerta que avisa en 10 minutos vale más que un dashboard precioso que nadie abre.
Si tu sistema mueve dinero, puedes ir más allá y poner un límite duro. Por encima de cierto monto, la operación queda pendiente hasta que un humano la apruebe. ¿Molesto? Un poco. Pero aprobar un retiro sale mucho más barato que explicarle a un cliente por qué desapareció su dinero.
Lo que yo cambiaría hoy en tu proyecto
Si hoy tienes 30 minutos libres, lo haría en este orden:
- Correr
bun audity leer el resultado de verdad. - Revisar
trustedDependenciesy sacar lo que no necesita estar ahí. - Agregar
--frozen-lockfileal pipeline. - Buscar secretos filtrados en el historial de git (
git log -p | grep -i "sk_live"ya asusta a mucha gente). - Crear una alerta, una sola, para la operación más sensible de tu sistema.
Ninguno de estos pasos depende de que trabajes con cripto. Todos reducen el daño si algún día alguien entra.
Mi opinión sobre el caso
Todavía falta el informe oficial para saber qué pasó. Puede ser una clave filtrada, puede ser ingeniería social, puede ser algo nuevo. Mi apuesta es que va a ser algo aburrido, porque casi siempre lo es.
Lo que me molesta de este tipo de noticias es la reacción de siempre: todos comentan el tamaño de la cifra y casi nadie revisa su propio proyecto. Sea una clave de US$ 5.600 millones o de 5.000 dólares, el error suele ser el mismo. La diferencia es que el tuyo no llega a los titulares, solo se convierte en un fin de semana perdido.
Bun ayuda con valores por defecto más seguros en la instalación y con Bun.secrets. Pero ninguna herramienta resuelve una clave pegada en un archivo olvidado. Eso lo resuelve tu disciplina. Si quieres ver cómo lo aplico en producción, échale un vistazo a los proyectos que mantengo.
Resumen para LinkedIn
US$ 5.600 millones salieron de un exchange gigante, y apuesto a que el error fue algo bastante aburrido. Todavía no hay informe oficial sobre el hackeo de Binance. Pero casi todo gran robo empieza con una clave filtrada, una dependencia maliciosa o la falta de una alerta. Puede que tu proyecto no maneje miles de millones, pero tiene una clave de Stripe, un token del banco y acceso a AWS. El ataque es el mismo. Solo cambia el tamaño del daño. En Bun empiezo por lo básico: trustedDependencies reducido al mínimo, --frozen-lockfile en el CI, bun audit y Bun.secrets para sacar la clave del .env. Y una sola alerta, en la operación más sensible. Ver el dinero salir después no te devuelve nada. Escribí el paso a paso completo en el blog. Si hoy tienes 30 minutos, vale la pena revisar tu proyecto. #Bun #SeguridadInformática #JavaScript #Backend #DevSecOps