La fijación de dependencias, es decir, especificar números de versión exactos en lugar de rangos de versiones en las especificaciones de dependencias, es la práctica que garantiza que se resuelvan las mismas versiones de las dependencias en cada compilación, en cada entorno y en todo momento. Las especificaciones de versión flotante (que usan >= o ~= u operadores de rango similares que permiten al gestor de paquetes resolver versiones compatibles más recientes) crean implementaciones en las que el software que se ejecuta en producción puede ser distinto del software que se probó, porque se publicó una nueva versión de una dependencia entre la ejecución de las pruebas y la implementación en producción.
El riesgo concreto es el mecanismo de cambio fantasma de dependencias. Cuando un pipeline de CI/CD está configurado con especificaciones de versión flotantes, las dependencias resueltas durante la ejecución de pruebas pueden diferir de las que se resuelven cuando el mismo pipeline se ejecuta de nuevo al día siguiente, si algún paquete que coincide con una especificación flotante publicó una nueva versión entretanto. La ejecución de pruebas resuelve las dependencias disponibles en el momento de la prueba; el despliegue en producción resuelve las dependencias disponibles en el momento del despliegue. Sin archivos de bloqueo que registren las versiones exactas resueltas, ambos conjuntos de dependencias pueden ser distintos, y la diferencia no será visible en ninguna comparación del código de la aplicación.
La implicación para la seguridad de la cadena de suministro va más allá de los errores. Una especificación de versión flotante que resuelve una nueva versión de una dependencia es un mecanismo por el cual un atacante de la cadena de suministro puede llegar a las implementaciones de producción: basta con publicar una versión maliciosa de un paquete que satisfaga una especificación flotante de uso generalizado para que cualquier compilación que se ejecute después de la publicación resuelva esa versión, hasta que la versión maliciosa se retire, se marque o la sustituya una versión superior. La fijación de dependencias no elimina por completo este riesgo (un desarrollador puede seguir actualizando las dependencias fijadas si incorpora intencionalmente una nueva versión), pero impide la resolución automática de nuevas versiones sin una acción explícita del desarrollador.
Por qué es importante
La fijación de dependencias es importante para TPRM porque las especificaciones de versión flotante en el código de los proveedores significan que el software que un proveedor entregó ayer puede ser distinto del que compile mañana, aunque no se haya cambiado nada del código de la aplicación. Para la seguridad de la cadena de suministro, la reproducibilidad de la resolución de dependencias es un control fundamental. Los proveedores que usan especificaciones flotantes sin imponer archivos de bloqueo están construyendo software cuya composición cambia con cada compilación.
- Especificaciones de versión flotantes: el software resuelto cambia con cada nueva publicación de paquetes
- Archivo de bloqueo ausente o no aplicado: no hay registro de las versiones exactas resueltas
- Divergencia de dependencias entre pruebas y producción: se prueban y despliegan versiones distintas
- Vector de ataque a la cadena de suministro mediante especificaciones flotantes: una versión maliciosa cumple el rango
- Reproducibilidad de las compilaciones socavada por dependencias no fijadas
Cómo se ve una buena práctica
Los programas maduros de gestión de dependencias usan la fijación de versiones exactas en las especificaciones de dependencias o imponen el uso de archivos de bloqueo, lo que garantiza que la resolución de dependencias sea determinista y que las versiones exactas resueltas queden registradas y sean reproducibles en los entornos de pruebas, preproducción y producción.
- Fijación de versiones exactas o uso obligatorio del archivo de bloqueo para todas las dependencias de producción
- Archivo de bloqueo incluido en el control de versiones: versiones resueltas registradas
- Diferencias del archivo de bloqueo en la revisión de código: cambios de dependencias visibles y revisados
- Actualizaciones deliberadas de dependencias: se requiere la intención del desarrollador, no una resolución automática
- Verificación de la fijación en CI: las compilaciones fallan si el archivo de bloqueo no está actualizado
Herramientas
Fijación de dependencias: npm ci (usa package-lock.json), pip install -r requirements.txt con hashes, Pipenv/Poetry para la fijación en Python
Los gestores de paquetes propios de cada lenguaje ofrecen mecanismos de archivo de bloqueo que registran las versiones exactas resueltas de las dependencias y obligan a usarlas. npm ci instala desde package-lock.json en lugar de resolver desde package.json, lo que impide la resolución de especificaciones flotantes. pip install con verificación de hash en requirements.txt fija tanto la versión como el hash. Para los profesionales de TPRM, preguntar si los proveedores aplican el uso obligatorio del archivo de bloqueo en las compilaciones de producción ofrece una pregunta concreta sobre la fijación de dependencias.
Desafíos de gobernanza
El desafío de gobernanza de la fijación de dependencias es la fricción de las actualizaciones de seguridad. Las dependencias fijadas no incorporan automáticamente los parches de seguridad: el desarrollador debe actualizar el archivo de bloqueo de forma explícita. Esta fricción lleva a algunos equipos a preferir especificaciones flotantes para que los parches de seguridad se resuelvan automáticamente. La solución de gobernanza son las herramientas de actualización automatizada de dependencias, como Dependabot o Renovate, que abren solicitudes de incorporación de cambios (pull requests) para actualizar el archivo de bloqueo con parches de seguridad, lo que combina la rapidez de respuesta ante parches de las especificaciones flotantes con la reproducibilidad de despliegue de las versiones fijadas.
- Imponga archivos de bloqueo en las compilaciones de producción: las especificaciones flotantes son aceptables en desarrollo con un archivo de bloqueo
- Incorpore los archivos de bloqueo al control de versiones: un historial visible de los cambios de dependencias
- Implemente Dependabot o Renovate: PR automatizadas de actualizaciones de seguridad con actualización de los archivos de bloqueo
- Incluya la revisión de los archivos de bloqueo en los requisitos de revisión de código
- Verificación de la fijación en el pipeline de CI: la compilación falla si el archivo de bloqueo diverge
Si usted forma parte de un equipo pequeño
Compruebe si los repositorios de sus proveedores de software más críticos contienen archivos de bloqueo: package-lock.json para npm, Pipfile.lock o requirements.txt con hashes para Python, Gemfile.lock para Ruby. La presencia de un archivo de bloqueo confirmado en el repositorio indica que las dependencias están fijadas. Pregunte a los proveedores si sus compilaciones de producción imponen el uso del archivo de bloqueo (por ejemplo, npm ci en lugar de npm install en el caso de npm). La confirmación del archivo de bloqueo es visible en una revisión del repositorio que lleva segundos.
- Verifique que los repositorios del proveedor tengan archivos de bloqueo confirmados
- Preguntar si las compilaciones de producción imponen el uso del archivo de bloqueo (npm ci, etc.)
- Preguntar por las herramientas de actualización de seguridad automatizadas: Dependabot o Renovate
- Incluir el uso obligatorio del archivo de bloqueo en la evaluación de la cadena de suministro de los proveedores
Qué exigir
Pregunte directamente:
“¿Sus compilaciones de producción aplican el uso obligatorio del archivo de bloqueo? En concreto, ¿utilizan npm ci en lugar de npm install, pip install con verificación de hash o mecanismos equivalentes que garanticen que se instalen las mismas versiones de dependencias en cada compilación?”
Evidencia que debe esperar
- Existencia de archivos de bloqueo en los repositorios de producción
- Uso obligatorio del archivo de bloqueo en el pipeline de CI/CD
- Herramientas de actualización automatizada de dependencias: Dependabot o Renovate
- Visibilidad de los cambios del archivo de bloqueo en la revisión de código
A un proveedor que confirma que gestiona sus dependencias se le debe preguntar por la imposición de archivos de bloqueo. Las especificaciones de dependencias definen qué versiones son aceptables. Los archivos de bloqueo registran qué versiones están realmente instaladas. Las compilaciones de producción que usan archivos de bloqueo garantizan que se instale el mismo software cada vez.
Cómo demostrarlo
- Verificación de la imposición de archivos de bloqueo
- Revisión de archivos de bloqueo en el repositorio
- Implementación de actualizaciones de seguridad automatizadas
- Verificación de versiones fijadas en CI
Conclusión clave
Especificación de versión: >=2.0.0. Se publicó una nueva versión el jueves. Las pruebas pasaron el jueves. El viernes se desplegó en producción un software distinto. No cambió ninguna línea de código. Cambiaron las dependencias resueltas. Las especificaciones de versión flotantes permiten que el gestor de paquetes resuelva automáticamente versiones compatibles más recientes. El uso obligatorio del archivo de bloqueo lo impide: se instalan las versiones exactas registradas en el archivo de bloqueo, en lugar de resolverse de nuevo en cada compilación. El atacante de la cadena de suministro que publica una versión maliciosa que cumple una especificación flotante llega a todas las compilaciones que se ejecuten después de la publicación. El uso obligatorio del archivo de bloqueo exige una acción deliberada del desarrollador para actualizar las versiones fijadas, lo que convierte un vector de ataque automático en un flujo de actualización deliberado.