La mayoría de los grandes ataques a la cadena de suministro de los últimos dos años funcionaron igual. Un desarrollador ejecutó npm install y una dependencia que nunca eligió ejecutó código en su máquina antes de que escribiera una sola línea. npm lo permite por diseño y ahora está dando marcha atrás en esa decisión. Go nunca lo permitió. Este artículo analiza cuánto explica esa única diferencia, en qué es Go de verdad más seguro y en qué afronta los mismos riesgos que Node. Cada afirmación va acompañada de su fuente.
Resumen rápido
Go vs Node.js: la seguridad de la cadena de suministro de un vistazo
| Aspecto | Go (Golang) | Node.js (npm) |
|---|---|---|
| Ejecución de código al instalar | Ninguna. go get/go build no ejecutan scripts de paquetes. | preinstall/install/postinstall ejecutan código arbitrario automáticamente. |
| Cuándo se ejecuta el código malicioso | Solo cuando se importa y se llama a la función. | En el momento de instalar, antes de que escribas una línea. |
| Garantía de integridad | Registro de transparencia de checksums basado en un árbol de Merkle (sum.golang.org). Cualquier manipulación queda a la vista para siempre. | Hashes en el lockfile y, más recientemente, procedencia. Históricamente más débil. |
| Resolución de versiones | Minimal Version Selection (selección de versión mínima). Las actualizaciones son explícitas. | Rangos semver (^, ~). Un parche malicioso nuevo puede resolverse automáticamente. |
| Superficie de dependencias | Biblioteca estándar grande, menos dependencias directas. | Cultura de módulos diminutos, árboles transitivos enormes. |
| Escaneo de vulnerabilidades | govulncheck hace análisis de alcanzabilidad, así que da menos falsas alarmas. | npm audit señala todo el árbol, lo que lleva a la fatiga de alertas. |
| Modelo de registro | Descentralizado. Los módulos son URL de código fuente. | Cuentas centralizadas. Un solo phishing expone todo el catálogo de un mantenedor. |
| Sigue expuesto a | Mantenedores víctimas de phishing, typosquats, malware almacenado en la caché del proxy. | Todo lo anterior, más gusanos basados en scripts de instalación. |
Go sale ganando en comportamiento por defecto y en superficie de ataque. Un atacante decidido que engaña con phishing a un mantenedor humano puede colarse igualmente en cualquiera de los dos ecosistemas.
¿Por qué Go evita el ataque que más daño hace a npm?
Go no tiene scripts de instalación. Cuando ejecutas npm install, cada paquete del árbol transitivo puede registrar hooks de ciclo de vida (preinstall, install, postinstall). npm los ejecuta automáticamente, con los privilegios de tu usuario, antes de que hayas ejecutado o siquiera leído una sola línea (documentación de scripts de npm). GitHub ha calificado los scripts de instalación como la mayor superficie de ejecución de código del ecosistema npm (GitHub Security, 2025).
Go lo dejó fuera a propósito. No tiene hooks de compilación. go get descarga y verifica el código fuente, go build lo compila, y ninguno de los dos ejecuta scripts definidos por el paquete. Una dependencia de Go envenenada se queda ahí como simple código fuente hasta que tu código la importa y llama a la función maliciosa. Esa ventana es mucho más estrecha y más fácil de detectar que un código que se ejecuta para todos en cada instalación.
Esto es lo que hace cada comando:
example.bashbash# npm: the postinstall hook runs on every install, automatically $ npm install left-pad > [email protected] postinstall > node ./scripts/setup.js # <- arbitrary code, your privileges, right now # go: nothing in the package gets to run during fetch or build $ go get example.com/some/module go: downloaded example.com/some/module v1.0.0 # source fetched, checksum verified, nothing executed
Por esta diferencia npm v12, que sale en julio de 2026, bloquea los scripts de instalación por defecto, y pnpm ya hizo lo mismo en la v10 (documentación de scripts de npm). Las herramientas de JavaScript se están acercando al comportamiento que Go tenía desde su lanzamiento.
¿Cuán grave es el problema de la cadena de suministro de npm según las cifras?
npm es con diferencia el mayor objetivo. Sonatype identificó 454.648 paquetes maliciosos nuevos en 2025, lo que elevó su total acumulado de paquetes bloqueados por encima de 1,23 millones, un aumento interanual del 75 %. Más del 99 % de ese malware de código abierto estaba en npm (Sonatype, 11.º informe State of the Software Supply Chain, 2026). Su índice de malware del cuarto trimestre de 2025 situó la cifra en el 99,8 % del malware bloqueado ese trimestre.
No hay ningún recuento publicado de módulos maliciosos de Go. Sonatype, OpenSSF y la mayoría de los proveedores no siguen Go como ecosistema aparte en sus índices de malware, porque hay demasiado pocos incidentes para indexarlos. Ese hueco en los datos ya dice mucho por sí solo.
Dónde estuvo el malware de código abierto en 2025 (porcentaje de paquetes maliciosos nuevos, por ecosistema):
| Ecosistema | Porcentaje del malware de código abierto de 2025 |
|---|---|
| npm | Más del 99 % |
| Todos los demás (incl. Go)* | Menos del 1 % |
*En 2025 se identificaron 454.648 paquetes maliciosos nuevos. Go no figura como ecosistema aparte, porque hay demasiado pocos incidentes para indexarlos. Fuente: Sonatype, 11.º informe State of the Software Supply Chain, 2026.
¿Qué pasó realmente en la ola de gusanos de npm de 2025 y 2026?
Una serie de gusanos que se propagaban solos convirtió un riesgo conocido en una emergencia constante, y todos ellos usaron scripts de instalación. npm lleva años sufriendo incidentes de gran repercusión. En 2018, event-stream escondió un malware que robaba carteras de Bitcoin en una dependencia descargada unos 8 millones de veces. En 2021, ua-parser-js distribuyó criptomineros tras el robo de una cuenta. Pero fue en 2025 cuando los incidentes empezaron a llegar uno tras otro.
| Fecha | Incidente | Qué pasó |
|---|---|---|
| 8 sept. 2025 | qix, víctima de phishing | 18 paquetes secuestrados, entre ellos chalk y debug. Unos 2.600 millones de descargas semanales expuestas. |
| sept. 2025 | Shai-Hulud | Primer gusano de npm. Un hook postinstall recopilaba credenciales y se inyectaba en unos 100 paquetes más por víctima. |
| 24 nov. 2025 | Shai-Hulud 2.0 | Pasó a preinstall y añadió el borrado del directorio personal. Unos 796 paquetes, presentes en el 27 % de los entornos en la nube analizados. |
| 31 mar. 2026 | axios | Actores norcoreanos manipularon al mantenedor con ingeniería social. Un RAT en postinstall provocó contactos con servidores C2 en más de 12.000 proyectos. |
| jun. 2026 | Miasma | Esquivó el bloqueo de scripts de instalación de npm con la técnica «Phantom Gyp», una recompilación implícita de node-gyp. |
El ataque a qix de septiembre de 2025 empezó con un convincente correo de restablecimiento de 2FA enviado desde el dominio falsificado npmjs.help. El atacante se hizo con la cuenta del mantenedor Josh Junon y publicó versiones maliciosas de 18 paquetes. Entre ellos estaban chalk (~300 millones de descargas por semana) y debug (~358 millones), para una exposición combinada de más de 2.600 millones de descargas semanales. La carga útil era un crypto-clipper que actuaba en el navegador, y estuvo activa unas dos horas.
Pocos días después llegó Shai-Hulud, el primer gusano de npm propiamente dicho. Al instalarse, su bundle.js recopilaba credenciales de npm, GitHub, AWS y GCP y ejecutaba TruffleHog para extraer secretos. Después usaba los tokens de npm robados para inyectarse en hasta unos 100 paquetes más de cada víctima. CISA emitió una alerta (CISA, 2025). Sonatype atribuye 171.740 paquetes maliciosos a campañas autorreplicantes de npm en el transcurso de unos pocos meses.
Shai-Hulud 2.0 (24 de noviembre de 2025) trasladó la ejecución a preinstall para llegar a más máquinas. Instaló el runtime Bun para esquivar la supervisión basada en Node y añadió un mecanismo de respaldo destructivo capaz de borrar el directorio personal. Wiz encontró paquetes afectados en aproximadamente el 27 % de los entornos en la nube que analizó (Wiz, 2025).
En 2026 quedó claro que bloquear los scripts de instalación no bastaría por sí solo. El ataque a axios (31 de marzo de 2026) no empezó con una contraseña robada por phishing. Actores vinculados a Corea del Norte (identificados como UNC1069 / Sapphire Sleet) fueron a por el mantenedor principal con una empresa falsa, un espacio de trabajo de Slack con marca propia y una llamada de Teams que instaló un RAT en el equipo del mantenedor. Después publicaron versiones maliciosas de axios (más de 100 millones de descargas semanales) con un RAT en postinstall. StepSecurity detectó contactos anómalos con servidores C2 en más de 12.000 proyectos (StepSecurity, 2026). Luego Miasma (junio de 2026) esquivó el bloqueo de scripts de instalación que npm iba a introducir con la técnica «Phantom Gyp». El paquete incluye un archivo binding.gyp, y la recompilación implícita de node-gyp que hace npm ejecuta la carga útil sin ningún script declarado.
Entonces, ¿es Go una solución mágica? No del todo
Los valores por defecto de Go frenan toda una categoría de ataques, pero aun así los atacantes han ido a por Go unas cuantas veces. Los módulos maliciosos de Go documentados en 2025 se cuentan en unas pocas decenas, repartidos en un puñado de campañas. Al lado de los cientos de miles de npm, es una cifra minúscula. Vale la pena repasar el caso más notable, porque muestra cuánto trabajo tiene que hacer un atacante para sortear el diseño de Go.
La puerta trasera de boltdb-go/bolt se reveló en febrero de 2025, pero se había introducido ya en noviembre de 2021. Suplantaba mediante typosquatting al popular módulo BoltDB (github.com/boltdb/bolt), del que dependen miles de paquetes y que se usaba en Shopify y Heroku, e incluía una puerta trasera de comando y control. El atacante publicó la versión maliciosa v1.3.1, dejó que el Go Module Mirror la guardara en caché y después reescribió la etiqueta de Git para que apuntara de nuevo a código limpio. Quien auditaba el repositorio en GitHub veía código limpio, mientras el proxy seguía sirviendo la puerta trasera. Funcionó, pero solo explotando un caso límite del sistema de caché. En npm, un hook en tiempo de instalación habría hecho el trabajo sin ese esfuerzo (Socket, 2025).
En 2025 aparecieron algunas campañas más. En marzo hubo una oleada de typosquats de hypert y layout que llevaban malware de tipo loader dirigido a desarrolladores del sector financiero. También hubo algunos módulos que borraban discos y un programa que robaba credenciales SSH. Todos fueron casos aislados, y cada uno necesitaba que un desarrollador escribiera mal la ruta de importación. Ninguno se ejecutaba automáticamente.
Cuando se notifica un módulo malicioso, el equipo de seguridad de Go lo retira del proxy, que a partir de ese momento devuelve un 403 SECURITY ERROR, y lo añade a la base de datos de vulnerabilidades de Go. En el caso de boltdb-go, Google retiró el módulo tanto del proxy como de GitHub y lo registró en la base de datos de vulnerabilidades. También mencionó el trabajo en curso sobre análisis de capacidades con Capslock y sobre comparaciones con deps.dev. Las defensas de Go encarecen mucho los ataques, pero no garantizan la seguridad, y las de ningún otro ecosistema tampoco.
¿Qué hace que Go sea estructuralmente más seguro?
Además de no tener scripts de instalación, Go toma otras cuatro decisiones de diseño que suman a la ventaja.
La base de datos de checksums de Go es especialmente sólida. Tu go.sum guarda los hashes SHA-256 de cada dependencia. sum.golang.org es un registro de transparencia basado en un árbol de Merkle, al estilo de Certificate Transparency. Anota el hash de cada versión de un módulo la primera vez que alguien la descarga y lo conserva para siempre (referencia de módulos de Go). El comando go comprueba las pruebas de inclusión y de consistencia antes de confiar en cualquier código, así que una etiqueta de Git reescrita con force push o un proxy manipulado provocan un fallo evidente. El criptógrafo Filippo Valsorda, que dirigió el equipo de seguridad de Go en Google, sostiene que Go tiene la mejor garantía de integridad de paquetes de todos los ecosistemas de lenguajes, porque todos los clientes del mundo resuelven una versión concreta de un módulo a los mismos bytes, para siempre (Filippo Valsorda). El registro tiene un límite. Demuestra consistencia, no que el código sea bueno, y solo sirve si alguien lo vigila.
Minimal Version Selection frena las versiones maliciosas. Las compilaciones de Go usan la versión más baja que cumple todos los requisitos, y las actualizaciones son explícitas (referencia de MVS). Con los rangos ^ y ~ de npm, un parche malicioso recién publicado puede acabar automáticamente en una compilación. En Go, una versión nueva maliciosa no se extiende a los proyectos que dependen de ella hasta que una persona sube deliberadamente la versión requerida. Los gusanos qix y Shai-Hulud se apoyaban justo en la actualización automática que MVS no hace.
govulncheck usa análisis de alcanzabilidad para reducir las falsas alarmas. El escáner oficial de Go consulta la base de datos de vulnerabilidades curada de vuln.go.dev y solo avisa cuando tu código llama de verdad al símbolo vulnerable (govulncheck, blog de Go). npm audit señala cada versión vulnerable en cualquier parte del árbol, llegues o no a usarla, y los desarrolladores aprenden a ignorarlo.
No hay una cuenta central cuyo robo exponga todo un catálogo de paquetes. Los módulos de Go se identifican por la URL de su código fuente, así que dependen de la seguridad de la cuenta de GitHub (o de GitLab). En npm, una sola cuenta tomada expone todo lo que tiene ese mantenedor. Esa diferencia explica en parte por qué los gusanos de npm se propagan como lo hacen mientras los incidentes de Go quedan contenidos.
La biblioteca estándar más grande también ayuda. HTTP, JSON, criptografía y plantillas vienen incluidos en Go, así que un proyecto típico tiene menos dependencias directas y menos mantenedores en los que confiar. Un estudio de 2025 sí concluyó que la amplificación por dependencia de Go (alrededor de 4,48x) está cerca de la de npm (alrededor de 4,32x), así que cada paquete de Go no arrastra menos dependencias transitivas que uno de npm. Go sale ganando porque los proyectos empiezan con menos dependencias directas, y eso reduce el número de personas en las que confías.
Lo que los valores por defecto de Go no cubren
Go cierra automáticamente las puertas más grandes. Los riesgos siguientes afectan a todos los ecosistemas, Go incluido, así que tienes que cubrirlos tú mismo.
- Un mantenedor víctima de phishing puede saltarse las defensas de cualquier ecosistema. Si alguien se hace con la cuenta de GitHub de un mantenedor, puede etiquetar una versión maliciosa, y la base de datos de checksums la registrará fielmente porque no distingue el código bueno del malo. Un ataque al estilo de axios funciona contra todos los ecosistemas, y por eso la industria está adoptando llaves de hardware y la publicación de confianza (trusted publishing).
- Sigues eligiendo tus dependencias por su nombre. La verificación de checksums de Go impide que se manipule un módulo que tú elegiste, pero no puede impedir que elijas un typosquat. Comprueba la ruta de importación contra el repositorio canónico antes de añadir una dependencia.
- La inmutabilidad del proxy es, sobre todo, una ventaja. La caché que garantiza que cada compilación obtenga bytes idénticos es también lo que mantuvo accesible la puerta trasera de boltdb-go después de que se limpiara la etiqueta de Git. Fue un caso excepcional, y el proceso de retirada del equipo de Go junto con la base de datos de vulnerabilidades lo cubren.
- Los valores por defecto protegen la instalación, no la ejecución. La ventaja de Go es que nada se ejecuta durante la descarga o la compilación. En cuanto importas y llamas a una dependencia, esta se ejecuta con tus privilegios, igual que en cualquier lenguaje. Aislar código no confiable en tiempo de ejecución es otro problema, en cualquier ecosistema.
- Mantén la protección activada. El único error autoinfligido es configurar
GOSUMDB=offde forma generalizada, lo que desactiva la verificación de checksums descrita arriba. Déjala activada y limitaGOPRIVATEsolo a rutas que de verdad sean internas.
¿Cómo convergen ambos ecosistemas en las mismas soluciones?
Los dos ecosistemas están adoptando las mismas defensas, porque en ambos el punto débil son las personas. Tras la ola de 2025, GitHub anunció la autenticación de dos factores obligatoria con FIDO/WebAuthn, tokens granulares de corta duración y la retirada de los tokens clásicos heredados. También está impulsando trusted publishing mediante OIDC para obtener procedencia criptográfica de las compilaciones (GitHub Security, 2025). npm v12 (julio de 2026) bloquea los scripts de instalación por defecto y añade un periodo de espera min-release-age para que un paquete que solo estuvo publicado dos horas nunca llegue a ti.
Estos cambios ayudan. Pero axios mostró dónde dejan de funcionar. Ninguna política de tokens detiene a un mantenedor cuyo equipo está ejecutando un RAT. Solo lo habrían evitado las llaves de hardware FIDO2 combinadas con instalaciones con procedencia verificada y periodo de espera. Ambas comunidades están aprendiendo que los atacantes van más a por el mantenedor que a por las herramientas. Go sigue teniendo ahí una ventaja. Incluso cuando un mantenedor queda comprometido, el daño es menor, porque nada se ejecuta al instalar.
¿Qué deberías hacer ahora mismo?
Los pasos dependen de tu stack.
Si escribes Go:
- Nunca configures
GOSUMDB=offniGONOSUMCHECKde forma generalizada. LimitaGOPRIVATEestrictamente a rutas que de verdad sean internas. - Haz commit de
go.sumy compila con-mod=readonly. - Ejecuta
govulncheck ./...en CI como filtro que tiene en cuenta la alcanzabilidad. - Revisa las dependencias nuevas en busca de typosquats. Verifica las rutas de importación contra el repositorio canónico antes de añadirlas.
- Vigila las nuevas versiones de tus dependencias críticas con Socket o deps.dev.
Si escribes Node:
- Actualiza a npm 11.16.0+ y configura
ignore-scripts=true, con una lista de permitidos (como@lavamoat/allow-scripts) para los pocos paquetes que de verdad necesitan scripts. - Usa
npm cicon un lockfile congelado, fija versiones exactas y establece un periodo de esperamin-release-agede 3 a 7 días para que los ataques que solo duran dos horas nunca te alcancen. - Adopta trusted publishing (OIDC) y llaves de hardware FIDO2. No dependas de TOTP para publicar.
- Usa un análisis de composición de software de verdad (Socket, Snyk) con detección de comportamiento, no solo
npm audit. - Registra públicamente tus nombres de paquetes internos, como medida defensiva, para bloquear la confusión de dependencias.
Preguntas frecuentes
¿De verdad Go es más seguro que Node.js en la cadena de suministro?
Sí, por su estructura. La razón principal es que go get y go build no ejecutan scripts en tiempo de instalación, lo que elimina el vector preinstall/postinstall que hay detrás de casi todos los grandes gusanos de npm de 2025 y 2026. Más del 99 % del malware de código abierto de 2025 apareció en npm (Sonatype, 2026). Go es más seguro, pero no es inmune.
¿Cuál es la mayor diferencia de seguridad entre Go y npm?
La ejecución de código en tiempo de instalación. Al hacer npm install, npm ejecuta automáticamente scripts arbitrarios de los paquetes, con tus privilegios, para cada paquete del árbol. Go no tiene ningún hook de compilación, así que una dependencia maliciosa no hace nada hasta que tu código la importa y la llama. Por eso npm v12 bloquea ahora los scripts de instalación por defecto, que es como Go se ha comportado siempre.
¿Ha sufrido Go algún ataque a la cadena de suministro?
Sí. La puerta trasera de boltdb-go suplantó a BoltDB mediante typosquatting y sirvió una puerta trasera de acceso remoto desde la caché de módulos de Go durante tres años (Socket, 2025). Una campaña de 2025 con hypert/layout distribuyó al menos siete typosquats con malware de tipo loader. Estos incidentes son reales pero poco frecuentes, y los módulos maliciosos se retiran del proxy en cuanto se notifican.
¿Qué es la base de datos de checksums de Go y por qué importa?
sum.golang.org es un registro de transparencia basado en un árbol de Merkle. Guarda de forma permanente el hash de cada versión de un módulo la primera vez que se ve esa versión. El comando go verifica estas pruebas antes de confiar en el código, así que una manipulación o una etiqueta de Git reescrita provocan un fallo evidente (referencia de módulos de Go). Todos los clientes obtienen bytes idénticos para una versión dada, y esa es la garantía de integridad más sólida de la gestión de paquetes de uso general.
¿Bloquear los scripts de instalación de npm resuelve el problema?
Ayuda mucho, pero no lo arregla todo. El ataque a axios usó un RAT en postinstall después de manipular al mantenedor con ingeniería social, y la campaña Miasma esquivó el bloqueo de scripts mediante una recompilación implícita de node-gyp. Bloquear scripts, junto con llaves de hardware FIDO2, un periodo de espera tras cada publicación y la verificación de procedencia, cierra la mayor parte de la brecha. Un mantenedor víctima de phishing todavía puede saltarse cualquier control aislado.
¿Debería cambiar de Node a Go solo por seguridad?
La seguridad cuenta a favor de Go, pero la mayoría de los equipos eligen un lenguaje por todo lo que aporta: concurrencia, contratación, ecosistema y la rapidez con la que pueden entregar. Go es un lenguaje de backend sólido y además el que, por su estructura, resiste mejor los ataques a la cadena de suministro. Si ya te lo estás planteando, sus valores por defecto más seguros son un motivo más para aprenderlo.
Fuentes
Fuentes principales citadas en este artículo (última verificación: 29 de junio de 2026):
- Sonatype: informe State of the Software Supply Chain
- Socket: un módulo malicioso de Go explota la caché del proxy para persistir (boltdb-go)
- GitHub Security: nuestro plan para una cadena de suministro de npm más segura
- Referencia de módulos de Go: base de datos de checksums
- Referencia de módulos de Go: Minimal Version Selection
- Blog de Go: govulncheck
- Go: gestión de vulnerabilidades en Go
- Documentación de npm: scripts y hooks de ciclo de vida
- Alex Birsan: confusión de dependencias
- CISA: avisos de ciberseguridad
- Wiz: blog de investigación
- StepSecurity: blog
- Filippo Valsorda: words.filippo.io
