El pasado 7 de julio, Packagist, el registro oficial de paquetes de PHP, anunció un cambio que parece pequeño pero que toca los cimientos de cómo confiamos en las dependencias: las versiones estables pasan a ser inmutables. Una vez que publicas una versión, el commit al que apunta queda congelado para siempre. Nadie, ni siquiera el propio mantenedor, puede cambiarlo después.
Suena a tecnicismo, pero detrás hay una historia de ataques reales y una lección importante para cualquiera que tenga un composer.json en producción. Vamos por partes.
Qué significa "inmutable" en cristiano
Hasta ahora, cuando un paquete publicaba la versión v1.2.3, Packagist en realidad guardaba un puntero a una etiqueta (tag) de git. El problema es que ese puntero se podía mover. Si el mantenedor borraba el tag y lo volvía a crear apuntando a otro commit, la versión v1.2.3 pasaba a contener código distinto, con el mismo número de siempre.
A partir de ahora eso se acabó. El mapeo "versión igual a commit" queda sellado. En la práctica:
- Los retags quedan bloqueados. Si alguien intenta mover una etiqueta a otro commit, Packagist lo ignora, conserva la referencia original de confianza y marca la versión con un aviso de "retag blocked". El mantenedor recibe un correo avisando del intento.
- Los borrados dejan de ser destructivos. Ahora son borrados "blandos", con un motivo registrado (ya no existe en el repositorio, borrado por el mantenedor, retirado por un administrador, etc.) y se pueden recuperar. La versión aparece atenuada y deja de instalarse, pero su historia no desaparece.
- Todo queda en el registro público de transparencia. Borrados, recuperaciones y cambios bloqueados dejan una entrada permanente que cualquiera puede consultar.
Hay una sola excepción, y muy estrecha: se permite cambiar la URL de un paquete solo si el hash completo del commit sigue siendo idéntico. Es decir, cuando cambia la dirección del hosting pero el código es exactamente el mismo.
Por qué ahora: los ataques que lo provocaron
Este cambio no es un capricho de ingeniería. Es la respuesta directa a una racha de ataques a la cadena de suministro de PHP durante 2026.
El caso más sonado fue el de los paquetes laravel-lang el 22 y 23 de mayo. Según Snyk, se comprometieron más de 700 versiones históricas. Antes hubo otro incidente parecido con intercom/intercom-php a finales de abril. El patrón era siempre el mismo: los atacantes tomaban el control de una cuenta de GitHub o robaban un token, y con ese acceso reescribían etiquetas de versiones ya publicadas y muy instaladas para colar código malicioso, normalmente ladrones de credenciales.
Lo peligroso es que ni siquiera tener el composer.lock te salvaba. En cuanto alguien ejecutaba composer update, Composer refrescaba los metadatos y reescribía la entrada del lock apuntando a la versión ya envenenada. El commit "de confianza" que creías tener había cambiado bajo tus pies.
La inmutabilidad ataca exactamente ese vector. Si el mapeo versión a commit no se puede alterar, reescribir una versión de confianza deja de ser posible.
Lo que esto sí resuelve, y lo que no
Aquí conviene ser honestos, porque es fácil vender esto como la solución definitiva y no lo es.
Lo que resuelve. Cierra el ataque de reescritura de versiones publicadas, que era la debilidad estructural más grave. PHP arrastraba una fragilidad conocida frente a ecosistemas como npm o crates.io: en npm, cuando publicas una versión, el registro guarda el propio paquete y este es inmutable por diseño. Packagist no aloja el código, solo indexa git, así que lograr inmutabilidad "por encima" de git es una solución de ingeniería elegante y necesaria. Es una señal clara de madurez del ecosistema.
Lo que no resuelve. La inmutabilidad no impide que la primera publicación de una versión ya venga con malware. Si un atacante entra en una cuenta y publica una versión nueva y maliciosa, esa versión será inmutable, sí, pero maliciosa igualmente. Contra eso lo que sirve es otra capa de defensas que Packagist está desplegando en paralelo: el registro de transparencia (activo desde noviembre de 2025), la detección de malware integrada con Aikido, Composer 2.10 cerrando rutas de descarga alternativas, y sobre todo el doble factor obligatorio para mantenedores. La inmutabilidad es un cimiento, no un tejado.
El matiz que casi nadie menciona. Las versiones dev siguen siendo mutables. Una rama como dev-main es, por definición, un puntero que se mueve, así que queda fuera de esta protección. Si tienes dependencias apuntando a ramas de desarrollo en producción, este anuncio no te cubre en absoluto.
Qué deberías hacer en tu proyecto PHP
El anuncio es una buena excusa para revisar tres cosas:
- Evita depender de versiones dev en producción. Fija versiones estables siempre que puedas. Es donde vive la nueva protección.
- Activa el doble factor en las cuentas de tus propios paquetes. Si publicas librerías, tu cuenta es ahora el eslabón más débil, no la infraestructura de Packagist.
- Confía en el
composer.lock, pero entiende sus límites. Con versiones inmutables, tu lock por fin significa lo que creías que significaba: el mismo número de versión te dará siempre el mismo código.
En resumen
Packagist ha tapado uno de los agujeros más incómodos de la cadena de suministro de PHP, y lo ha hecho con una solución bien pensada para las particularidades de un registro basado en git. No es la bala de plata que resuelve toda la seguridad de dependencias, y sería un error contarlo así. Pero es un paso serio, forzado por ataques reales, que acerca al ecosistema PHP al nivel de garantías que otros lenguajes ya daban por sentado. Para quien construye sobre Laravel o Composer todos los días, es una de esas mejoras que trabajan en silencio y que solo notas el día que evitan un desastre.