PHP

Composer 2.10: bloqueo de malware y políticas de dependencias

Autorangel cruz
Publicado
Lectura9 min de lectura
Composer 2.10: bloqueo de malware y políticas de dependencias

Composer 2.10 ya está disponible y trae el cambio de seguridad más importante en años para el ecosistema PHP: bloqueo de malware nativo y un sistema unificado de políticas de dependencias. Composer es el gestor de dependencias estándar de PHP, y esta versión convierte la protección contra ataques de supply chain en comportamiento por defecto de la herramienta, en lugar de dejarla como responsabilidad del desarrollador.

No es una mejora cosmética: es la respuesta directa a los ataques de supply chain que golpearon a Packagist durante 2026, como el de laravel-lang (22 de mayo de 2026) o el de intercom/intercom-php (30 de abril de 2026).

En este artículo veo qué cambia realmente, cómo se configura el nuevo objeto config.policy, y qué conviene hacer hoy mismo en tus proyectos.

Por qué llega ahora: los ataques de 2026

El contexto importa, porque explica cada decisión de diseño de esta versión. Según el blog de Packagist, durante 2026 hubo varios ataques de supply chain contra el ecosistema PHP, ejecutados a través de cuentas de GitHub comprometidas y tokens de acceso robados. Dos casos concretos:

  • laravel-lang (22 de mayo de 2026): se publicaron versiones maliciosas tras un secuestro de cuenta.
  • intercom/intercom-php (30 de abril de 2026): compromiso similar basado en credenciales.

El patrón común es el más peligroso de todos: el re-tagging. El re-tagging es reescribir una etiqueta (tag) de git existente para cambiar el código al que apunta sin cambiar el número de versión. El atacante no publicaba un paquete nuevo, sino que reescribía una versión que ya era de confianza y que miles de proyectos tenían fijada en su composer.lock, inyectándole malware. Una versión que ayer era segura, hoy traía una puerta trasera bajo el mismo número.

Composer 2.10 ataca exactamente ese vector.

Bloqueo de malware nativo

La función central de esta versión es la detección de malware integrada, alimentada por el feed de Aikido (licencia CC-BY 4.0). Funciona así:

"Flagged versions are removed from the resolution pool, so they cannot be installed via composer update, composer require or composer create-project."

En claro: las versiones marcadas como maliciosas se eliminan del pool de resolución. No se pueden instalar con composer update, composer require ni composer create-project.

Lo más importante es que la protección también corre durante composer install:

"A malicious release that slipped into a lockfile will not be silently pulled in on CI runs or in production deployments."

Esto es clave. Si un release malicioso se coló en tu composer.lock, no se va a instalar de forma silenciosa en CI ni en producción. Justo el escenario del re-tagging que describí arriba.

composer audit ahora detecta malware

El comando composer audit se actualizó para reportar malware además de las advisories de seguridad de siempre:

# Falla por defecto si encuentra malware en el árbol de dependencias
composer audit

"The same versions are surfaced by composer audit, which fails the audit when finding malware by default."

Junto con esto cambió el comportamiento de los exit codes (un detalle a tener en cuenta en CI):

"composer audit exit codes now use 0 for success and 1 when the audit fails."

Si tienes un step de CI que corre composer audit, revisa que estés interpretando bien el código de salida: 0 es éxito, 1 es fallo.

config.policy: un solo objeto para todas las políticas

Aquí está el cambio estructural. Composer 2.10 introduce el objeto config.policy, que reemplaza la configuración anterior bajo config.audit y unifica tres políticas integradas bajo una misma estructura:

Política Qué cubre
malware Versiones marcadas como maliciosas
advisories Versiones con vulnerabilidades de seguridad conocidas
abandoned Paquetes marcados como abandonados

Cada política comparte la misma estructura, con tres opciones: block, audit e ignore. Y cada una llega con defaults sensatos:

Política Update Audit Install
malware bloqueado falla bloqueado
advisories bloqueado falla instalable (para evaluación)
abandoned solo lo reporta el audit reporta no bloqueado

La lógica detrás de los defaults es razonable: el malware se bloquea en todos lados sin excepción; una advisory te frena en el update y el audit, pero te deja instalar la versión si necesitas evaluarla; y un paquete abandonado solo se reporta, porque "abandonado" no significa "inseguro".

Políticas personalizadas

Además de las tres integradas, puedes definir tus propias políticas apuntando a una fuente HTTPS. Por ejemplo, una lista interna de paquetes vetados por tu organización:

{
    "config": {
        "policy": {
            "company-policy": {
                "sources": [
                    { "type": "url", "url": "https://example.com/bad-pkgs.json" }
                ],
                "audit": "fail"
            }
        }
    }
}

Esto abre la puerta a políticas corporativas centralizadas sin depender de herramientas externas.

Saltarse el bloqueo cuando hace falta

Si necesitas instalar algo bloqueado de forma puntual (por ejemplo, para reproducir o analizar un problema), existe el flag --no-blocking:

composer install --no-blocking

También funciona como variable de entorno, útil en entornos donde no controlas el comando directamente:

COMPOSER_NO_BLOCKING=1 composer install

Mi recomendación: úsalo solo de forma local y consciente, nunca en CI ni en producción. El bloqueo está ahí por una razón.

Inmutabilidad de versiones estables

Esta es la pieza que cierra el círculo contra el re-tagging. A partir de ahora, las versiones etiquetadas como estables no pueden reescribirse silenciosamente mediante un re-tag en git. Una versión publicada queda fija: si el contenido de un tag cambia, deja de ser válido en lugar de instalarse como si nada.

Combinado con el bloqueo durante install, esto significa que el escenario de laravel-lang (reescribir una versión que ya estaba instalada en miles de composer.lock) deja de ser viable de forma silenciosa.

Deprecación del source fallback

Composer 2.10 deprecó el comportamiento de source fallback, por el riesgo de seguridad que implica cuando la descarga de un artefacto falla en repositorios privados. Hay una nueva opción source-fallback para reactivar el comportamiento legacy si de verdad lo necesitas, pero se elimina por completo en Composer 2.11. Conviene ir migrando ya.

Otras mejoras de la versión

No todo es seguridad. La 2.10 trae también un par de mejoras de ergonomía que vale la pena conocer:

Wildcards en --with. Ahora puedes restringir varias dependencias de un namespace en una sola pasada:

composer update --with "acme/*:^2.0"

--require en create-project. Puedes añadir dependencias extra al crear un proyecto:

composer create-project acme/skeleton my-project --require="acme/extra-bundle:^1.0"

--bump-after-update más preciso. Ahora solo hace bump de los paquetes que realmente se actualizaron, no de todo el composer.json.

A esto se suman optimizaciones en el autoloading de plugins y una reducción del uso de memoria del PoolOptimizer.

Lo que viene (roadmap de Packagist)

El equipo de Packagist dejó claro que la 2.10 es un paso dentro de un plan más grande. Esto todavía no está disponible, pero está anunciado:

  • Política de antigüedad mínima (cooldown): rechazar la instalación de versiones publicadas hace muy poco, para dar margen a que se detecte malware antes de que llegue a producción.
  • MFA obligatorio en Packagist.org, con el estado de MFA visible en los perfiles de maintainer y registrado en el transparency log.
  • Organizational Package Ownership, para reemplazar las cuentas compartidas (uno de los grandes vectores de los ataques de 2026).
  • Flujo de release escalonado con FIDO2 para paquetes con base de usuarios grande.
  • Hosting directo de artefactos con provenance SLSA y attestations de Sigstore, alineándose con OpenSSF Level 3 y SLSA L3-L4.

Es una hoja de ruta seria. Vale la pena seguirla si mantienes paquetes propios.

Qué hacer hoy

Sin teoría, lo accionable:

  1. Actualiza Composer a 2.10: composer self-update.
  2. Corre composer audit en tus proyectos y en tu pipeline de CI. Revisa que el step interprete bien los exit codes (0 éxito, 1 fallo).
  3. Si mantienes paquetes en Packagist, habilita MFA ahora. La propia gente de Packagist lo pide de forma explícita: "If you maintain any package on Packagist.org and don't have MFA enabled, please enable it now."
  4. Confía en los defaults de config.policy. Están bien pensados; solo toca la configuración si tienes una necesidad real.
  5. Commitea tu composer.lock. Sigue siendo tu primera línea de reproducibilidad, y ahora el bloqueo durante install lo respalda. Si todavía tienes dudas sobre por qué, lo expliqué en detalle en La importancia del archivo composer.lock en PHP.

Preguntas frecuentes

¿Cómo actualizo a Composer 2.10?

Con composer self-update. Si lo instalaste vía package manager del sistema (apt, brew, etc.), actualízalo por esa misma vía.

¿El bloqueo de malware me puede romper un deploy en producción?

Puede frenar un composer install si detecta una versión marcada como maliciosa en tu composer.lock, y eso es justamente lo que quieres. No bloquea instalaciones legítimas: solo las versiones que están en el feed de malware. Si te frena, es señal de que tenías un release comprometido fijado.

¿De dónde salen los datos de malware?

Del feed de Aikido (licencia CC-BY 4.0), integrado en los metadatos de Packagist.org desde marzo de 2026. Packagist dejó abierta la puerta a sumar más proveedores con licencias libres adecuadas.

¿config.policy reemplaza a config.audit?

Sí. El objeto config.policy consolida bajo una misma estructura lo que antes estaba en config.audit, y suma las políticas de malware y paquetes abandonados junto con las advisories.

¿Puedo desactivar el bloqueo temporalmente?

Sí, con el flag --no-blocking o la variable de entorno COMPOSER_NO_BLOCKING. Úsalo solo de forma local y puntual, nunca en CI ni en producción.

¿Qué es el re-tagging y por qué importa tanto?

Es reescribir una etiqueta (tag) de git existente para cambiar el código que apunta sin cambiar el número de versión. Fue el vector de los ataques de 2026: una versión que ya estaba instalada en miles de proyectos se reescribía con malware. La inmutabilidad de versiones estables de Composer 2.10 corta ese vector.

En resumen

Cambio Estado
Bloqueo de malware en update/require/create-project Nuevo
Bloqueo de malware durante install (protege el lockfile) Nuevo
composer audit detecta malware Nuevo
Exit codes de composer audit (0/1) Cambio de comportamiento
Objeto config.policy unificado Nuevo (reemplaza config.audit)
Políticas personalizadas vía HTTPS Nuevo
Flag --no-blocking / COMPOSER_NO_BLOCKING Nuevo
Inmutabilidad de versiones estables Nuevo
Deprecación de source fallback Deprecado (se elimina en 2.11)
Wildcards en --with Nuevo
--require en create-project Nuevo

Composer 2.10 no es una versión más. Es el momento en el que el ecosistema PHP movió la seguridad de supply chain de "responsabilidad del desarrollador" a "comportamiento por defecto de la herramienta". Actualiza, corre composer audit, y si mantienes paquetes, habilita MFA hoy.

Fuentes

Sobre el autor
Angel Cruz

Angel Cruz

Soy desarrollador PHP senior. Casi todo lo que construyo pasa por Laravel, me obsesiona el código que se mantiene y escribo sobre lo que aprendo, con sus trade-offs incluidos.