15 puntos por GN⁺ 2025-11-22 | 3 comentarios | Compartir por WhatsApp
  • El cooldown de dependencias (dependency cooldown) es una técnica de seguridad simple y efectiva que puede mitigar la mayoría de los ataques a la cadena de suministro de software de código abierto
  • Los atacantes normalmente secuestran proyectos populares de código abierto para distribuir código malicioso, pero en la mayoría de los casos la ventana de exposición dura menos de una semana
  • Si se configura un cooldown que espere cierto tiempo después de publicar una nueva versión (por ejemplo, 7 días), se puede reducir significativamente el riesgo de infección por actualizaciones automáticas
  • Dependabot, Renovate y pnpm ya admiten funciones de cooldown de forma nativa, son fáciles de configurar y no tienen costo adicional
  • Si el cooldown se ofrece por defecto a nivel del gestor de paquetes, puede fortalecer la seguridad de la cadena de suministro y reducir alertas innecesarias

Estructura y problema de los ataques a la cadena de suministro

  • La mayoría de los ataques a la cadena de suministro (supply chain attack) siguen el mismo patrón
    • El atacante obtiene acceso aprovechando el robo de credenciales o una vulnerabilidad en CI/CD de un proyecto popular de código abierto
    • Sube cambios maliciosos al canal de distribución (PyPI, npm, etc.)
    • Los usuarios instalan la versión infectada debido a actualizaciones automáticas o a una falta de fijación de versiones
    • Un proveedor de seguridad lo detecta y emite una alerta, y luego el repositorio de paquetes elimina esa versión
  • El intervalo entre las etapas (1) y (2) suele ser largo, pero de la (2) a la (5) todo ocurre en cuestión de horas o pocos días, por lo que el periodo de actividad del atacante es corto
  • Ventana de oportunidad en casos importantes de los últimos 18 meses
    • xz-utils: unas 5 semanas
    • Ultralytics: 12 horas (etapa 1), 1 hora (etapa 2)
    • tj-actions: 3 días
    • chalk: menos de 12 horas
    • Nx: 4 horas
    • rspack: 1 hora
    • num2words: menos de 12 horas
    • Kong Ingress Controller: unos 10 días
    • web3.js: 5 horas
  • De estos casos, 8 tuvieron una duración del ataque de menos de una semana, y la mayoría podrían bloquearse con un cooldown

Concepto y efecto del cooldown

  • Un cooldown retrasa el uso de una nueva dependencia durante un periodo determinado después de su publicación
    • Durante ese tiempo, los proveedores de seguridad pueden detectar si es maliciosa
  • Ventajas
    • Es eficaz de forma empírica y bloquea la mayoría de los ataques a gran escala
    • Es muy fácil de implementar y en la mayoría de las herramientas puede configurarse gratis
    • Ejemplo en Dependabot
      version: 2
      - package-ecosystem: github-actions
        directory: /
        schedule:
          interval: weekly
        cooldown:
          default-days: 7
      
    • Incentiva conductas positivas en los proveedores de seguridad: los empuja a enfocarse en la detección rápida en vez de en alertas excesivas o promoción

Conclusión y recomendaciones

  • En 8 de 10 casos, los ataques duraron una semana o menos, y un cooldown de 7 días podría bloquear la mayoría
  • Si se aplica un cooldown de 14 días, se podrían defender todos los casos salvo xz-utils
  • El cooldown no es una solución perfecta, pero es una forma simple de reducir el riesgo de exposición entre 80% y 90%
  • Además de Dependabot y Renovate, hace falta mejorar el soporte para que los propios gestores de paquetes ofrezcan cooldown por defecto
  • La seguridad de la cadena de suministro no es solo un problema técnico, sino también de estructura de confianza social, pero el cooldown sigue siendo una mitigación realista y útil

3 comentarios

 
regentag 2025-11-23

De hecho, si no hay problemas, creo que es mejor no actualizar innecesariamente.
¿De verdad es necesario aplicar una versión nueva que no difiere mucho de la anterior, asumiendo ese riesgo?

 
GN⁺ 2025-11-22
Comentarios de Hacker News
  • A la gente le preocupa quedar expuesta a vulnerabilidades graves si no actualiza de inmediato, pero en realidad casi nunca es así
    Mucho software no se distribuye de forma continua, sino que el cliente instala manualmente cada nueva versión, así que se actualiza cada varias semanas o incluso meses
    Lo importante es el monitoreo de dependencias y revisar las vulnerabilidades publicadas. Si se evalúa si el producto realmente está afectado, entonces solo en ese momento hace falta actualizar esa dependencia de inmediato

    • En todo el ecosistema falta esta cultura de actualización selectiva
      Se ha extendido la idea de que si sale una nueva versión, hay que actualizar sí o sí hoy mismo
      Revisar sin ver los cambios reales, con la lógica de “hagámoslo ahora porque luego será más difícil”, es ineficiente
      Mantenerse en la punta del versionado incluso puede ser contraproducente para la seguridad
    • El problema realista es que muchos equipos de seguridad de grandes empresas imponen políticas de “zero CVE”
      En nuestra empresa, si el escáner detecta una vulnerabilidad crítica, hay que actualizar en un plazo de 7 días
      Si se pasa el plazo, se considera incumplimiento y arranca un proceso complicado, así que la mayoría simplemente actualiza todo de inmediato
    • La gente teme los 0-day, pero en la práctica la mayoría de los problemas proviene de vulnerabilidades sin parchear durante cientos de días
    • La clave es si la app recibe entradas desconocidas desde el exterior
      Las apps como los navegadores, que reciben mucho input externo, deben actualizarse seguido, pero en casos como una app del clima, donde el input está más limitado, el riesgo es relativamente menor
    • La estrategia de “actualizar solo cuando haga falta” suena bien en teoría, pero en la práctica el costo de evaluar cada vulnerabilidad para cada producto es demasiado alto
      Sale más eficiente actualizar periódicamente y combinar eso con medidas de defensa contra ataques a la cadena de suministro
  • Un modelo como Debian stable, donde la distribución administra las dependencias comunes y se hace una actualización integral cada ciertos años, parece cada vez más razonable
    Algunos ecosistemas se mueven demasiado rápido o tienen sistemas de empaquetado por distribución bastante deficientes
    Por ejemplo, sigue siendo difícil instalar bibliotecas de Node.js con apt y usarlas en un proyecto

    • No hay que confundir “movimiento” con “acción”
      Un ecosistema que se mueve rápido sin cambios de fondo no es saludable
      En JS casi no ha habido progreso real en los últimos 3 años, pero si intentas volver a compilar un proyecto de hace 3 años, el dolor es de nivel reescritura
    • Los resultados de búsqueda de paquetes node en Debian muestran que algunos existen, pero no es algo completo
      En distribuciones como Arch, a veces ni siquiera están
    • Con Rust se puede trabajar suficientemente bien solo con los repositorios de Debian
    • El modelo de versión estable implica la carga de que una app tenga que soportar al mismo tiempo versiones viejas y nuevas
  • Existe la idea de que poner un período de cooldown a las actualizaciones de dependencias para prevenir ataques a la cadena de suministro es mejor que usar de inmediato la versión más reciente para bloquear 0-days
    Se trata de comparar la probabilidad de que una actualización introduzca una nueva vulnerabilidad frente a la probabilidad de que corrija una vulnerabilidad existente
    Según SemVer, las versiones de parche son relativamente seguras, así que también se puede aplicar un cooldown corto para ese tipo de cambios
    Por ejemplo, si sale la 2.4.0 estando en la 2.3.4, y no hay una función urgente, puede ser mejor esperar hasta que salga la 2.4.1

    • En los 0-day publicados sí aparece un advisory de seguridad, así que herramientas como Dependabot omiten el cooldown y aplican el parche de inmediato
      La mayoría de las vulnerabilidades no viene de ataques intencionales, sino de bugs comunes
    • Los valores por defecto siempre se basan en supuestos. Si aparece información nueva, se cambian
  • Una política que limite la cantidad y complejidad de las dependencias es un enfoque todavía más fuerte
    En lugar de agregar bibliotecas que “hacen de todo”, habría que añadir solo dependencias pequeñas y con un propósito claro
    Además, si las bibliotecas ofrecieran versiones LTS que incluyan solo parches de seguridad, sería más fácil administrarlas

    • Este argumento aparece seguido, pero en la práctica reutilizar código ya probado es mucho más eficiente
      Reimplementar por cuenta propia puede ser un desperdicio. Da curiosidad saber qué ejemplos concretos hay de esas “everything library” problemáticas
    • La mayoría de los ataques a la cadena de suministro ocurre en la superficie de ingeniería social
      Si confías en el mismo desarrollador a través de varias bibliotecas, importan más las relaciones de confianza que la cantidad de paquetes
      La complejidad se correlaciona con las vulnerabilidades, pero no es la causa directa
    • Con la llegada de las herramientas de IA, bajó el costo de implementar por cuenta propia dependencias no esenciales
      Ahora es posible tomar decisiones pensando no solo en la velocidad a corto plazo, sino también en el mantenimiento a largo plazo
    • Dependencias demasiado fragmentadas pueden terminar aumentando tanto la cantidad como la carga de build
    • Es difícil justificar que una biblioteca de bajo nivel tenga a su vez otras dependencias
      En el mundo de C++, este punto incluso puede ser una ventaja competitiva
  • La presión por actualizar siempre a la última versión viene de la creencia equivocada de que el software siempre mejora
    En realidad, puede significar cambiar bugs conocidos por bugs nuevos y desconocidos
    Monitorear los issues públicos y parchear solo cuando haga falta es una postura razonable

    • Por ejemplo, en el caso de log4shell, log4j 1.x no era vulnerable, y el bug se introdujo en 2.x
      Es decir, hubo un caso en que una versión vieja terminó siendo más segura
    • Antes los intervalos entre releases eran más largos y las mejoras de calidad eran más claras, pero hoy la mayoría son cambios periféricos
      Tal vez también sea porque ya usamos software que de por sí es bastante estable
  • Si todos dicen “esperemos un poco”, al final puede pasar algo parecido a una tragedia de los comunes y nadie valida primero
    Cuanto más se espere, más deuda técnica se acumula, así que hacen falta actualizaciones graduales y mitigaciones como zero trust y monitoreo

    • Esperar aproximadamente una semana después de una nueva versión no genera una deuda técnica grande
      Para entonces, los escáneres de seguridad ya detectaron vulnerabilidades
    • En los ataques recientes a la cadena de suministro, la detección no vino por exposición del consumidor sino por dar tiempo de respuesta al mantenedor
    • Aunque baje el número de consumidores, eso igual les da tiempo a investigadores y mantenedores para analizar
      Si un atacante publica un release no autorizado, a veces se detecta de inmediato
    • Los sistemas grandes de todos modos hacen rollout gradual, así que una actualización total e inmediata ni siquiera es posible
  • La idea del cooldown es buena, pero existe el riesgo de que un atacante la explote para crear una falsa urgencia
    Puede empujar una instalación temprana diciendo “parche de seguridad urgente”, cuando en realidad esa versión es maliciosa
    Hay que prepararse para este tipo de ataques de presión psicológica

    • Si el atacante distribuye un exploit incluyendo también una corrección de bug, puede inducir a los desarrolladores a romper el cooldown e instalarlo
    • Surge la pregunta de “¿cómo generaría tanto ruido?”; es decir, la duda sobre cómo un atacante podría manipular la opinión general
  • Otra razón para el cooldown es darles tiempo a los mantenedores para darse cuenta por sí mismos de una intrusión
    Los atacantes apuntan a momentos en que el mantenedor está ausente, como vacaciones, conferencias o feriados
    Muchos proyectos están mantenidos por una o dos personas, así que esta diferencia de tiempo es muy importante

  • Depende de las características del proyecto y de su superficie de ataque
    Ya debería terminar la época de seguir simplemente las “mejores prácticas”
    La seguridad es como un deporte de contacto: hay que pensar críticamente en los detalles todos los días

  • También existe un límite del cooldown si se asume que solo con el paso del tiempo algo se vuelve seguro
    Si nadie revisa el código, una semana después el riesgo sigue siendo el mismo
    En cambio, un enfoque de despliegue gradual (gradual rollout) puede ser efectivo
    Cada consumidor puede configurar un factor de demora, de modo que quienes toleran más riesgo sean los primeros en encontrarse con los problemas,
    mientras tanto el resto queda protegido
    Las actualizaciones peligrosas se eliminan de la cola, por lo que los consumidores con retraso ni siquiera llegan a verlas

 
aer0700 2025-11-23

Últimamente, a veces me confundo sobre qué es más llevadero: simplemente reinventar la rueda o lidiar con el Tetris de las dependencias.
Porque, si algo sale mal por un if for while, basta con corregir mi código, pero cuando una rueda del Tetris de dependencias se desajusta de repente, hasta depurarlo se vuelve difícil.