Opciones de repositorios privados de PyPI y las limitaciones de cada uno de ellos

Ya están disponibles los parches de php2shell (CVE-2026-109618 y CVE-2026-109619) para PHP en fin de vida útil. Dado que las respuestas del departamento de ventas están tardando más de lo habitual debido a la elevada demanda, la forma más rápida de obtener los paquetes parcheados es comprar ELS para Runtimes en línea (se abre en una pestaña nueva). Lee las preguntas frecuentes sobre los parches

Ir directamente al resumen generado por IA de esta entrada del blog

  • Resume esta entrada con Perplexity
  • Resume esta publicación con Gemini

Principales conclusiones

  • Un repositorio privado de PyPI responde a la pregunta de dónde proceden los paquetes, pero no si es seguro instalarlos.
  • Añadir «–extra-index-url» en lugar de dirigir «–index-url» a un único índice vuelve a abrir la brecha que se pretendía cerrar con la creación del repositorio privado.
  • Un servidor espejo o caché sirve todo lo que se haya publicado en el servidor de origen, incluidas las versiones maliciosas.
  • Para vincular un paquete instalable a una fuente verificada es necesario realizar un paso de verificación explícito, no un cambio en la configuración.

Poner en marcha un repositorio privado de PyPI parece una tarea sencilla. Se configura DevPI o Artifactory, se incluye la URL interna en la documentación de incorporación y se cierra el ticket. Se acabó que los desarrolladores descarguen directamente desde el PyPI público. El auditor obtiene una respuesta. Se acabó la pesadilla.

Excepto que el repositorio privado solo es un espejo del PyPI público. La pregunta original ya tiene respuesta: el almacenamiento en caché funciona, pero hay un punto de control de políticas. Sin embargo, aún no se ha abordado la cuestión de si un paquete concreto es fiable. Simplemente se ha trasladado.

Esa es la parte que conviene analizar a fondo antes de elegir una arquitectura. Los cuatro enfoques habituales para un repositorio PyPI privado difieren enormemente en cuanto a lo que verifican, y la verificación brilla por su ausencia en los modelos básicos de espejo y proxy.

Qué problemas resuelve realmente un repositorio privado de PyPI

Cuatro problemas, y son reales.

Disponibilidad, para que una interrupción del servicio público de PyPI no afecte a tus implementaciones. Ancho de banda y velocidad, porque descargar las mismas ruedas mil veces al día de Internet es un desperdicio. Distribución interna de paquetes, para que las bibliotecas privadas tengan un lugar donde alojarse, además de una URL de Git oculta en el archivo requirements.txt. Y una punto de control de políticas, que es lo que realmente importa en materia de seguridad.

Lo que no resuelve por sí solo es la integridad de los paquetes. Una caché de «pull-through» recupera los datos del servidor de origen, almacena el resultado y lo sirve. Si el servidor de origen sirviera malware durante tres horas, la caché conserva fielmente ese malware y sigue sirviéndolo a todo el mundo a partir de entonces, con una latencia excelente.

Un repositorio privado de PyPI cambia la procedencia de tus paquetes. No cambia lo que son. Considerar el propio repositorio como un control de seguridad es un error arquitectónico habitual en este ámbito.

La proporción de paquetes maliciosos que llegan a los registros públicos ha aumentado año tras año, y un servidor espejo situado más adelante en esa cadena de crecimiento lo hereda todo por diseño. No hay nada en el mecanismo de duplicación que filtre según la intención.

El error de la URL fuera del índice que lo echa todo por tierra

pip tiene dos opciones que parecen intercambiables. No lo son, y la diferencia pasa fácilmente desapercibida… hasta el día en que alguien la aprovecha.

# Sustituye el índice predeterminado. Solo se consulta tu repositorio.
pip install –index-url https://pypi.internal.example.com/simple/ mypackage

# Añade un segundo índice. pip busca en ambos, sin dar prioridad a ninguno de ellos.
pip install –extra-index-url https://pypi.internal.example.com/simple/ mypackage

–extra-index-url suele aparecer porque –index-url por sí solo provocó algún fallo inicialmente. No se encontró un paquete, la compilación falló y, al añadir un segundo índice, el error desapareció.

He aquí por qué es importante. pip no tiene en cuenta de qué índice debe proceder un paquete. Su propia documentación es muy clara al respecto: no existe un orden de prioridad entre las ubicaciones en las que busca, por lo que se comprueban todas y se elige la que mejor se ajusta al requisito, lo que en la práctica significa la versión compatible más reciente. Si un atacante publica la versión 99.0.0 de «internal-billing-utils» en el PyPI público, pip la preferirá sin dudarlo a tu versión interna 2.4.1.

Eso es confusión de dependencias, y «-extra-index-url» es la forma en que se introduce.

Comprueba cómo están configurados realmente tus agentes de compilación, no lo que dice la documentación. Las variables de entorno PIP_INDEX_URL y PIP_EXTRA_INDEX_URL anulan de forma silenciosa el archivo pip.conf, y una imagen base heredada de otro equipo es una forma habitual de que esto pase desapercibido.

python -m pip config debug
pip config get global.index-url

El comando «pip config debug» muestra de qué archivo o variable procede cada configuración, lo que suele ser más rápido que ir adivinando. Aprovecha para comprobar el entorno de ambas variables de PIP mientras estás ahí.

La solución consiste en utilizar exclusivamente –index-url y hacer que tu repositorio privado sea el único del que pip tenga constancia, de modo que tanto los paquetes internos como los de origen se sirvan a través de él. La PEP 708 propuso unos metadatos de repositorio que permitirían a los índices indicar estas relaciones y eliminar las conjeturas a la hora de fusionarlos, pero tras tres años en estado provisional fue rechazada en 2026 porque las implementaciones necesarias nunca llegaron a materializarse. La norma del índice único es la que se aplica actualmente.

Cuatro formas de gestionar un repositorio PyPI privado

Las opciones se diferencian por lo que hacen con los paquetes de origen, no por el nombre del producto que aparece en la caja.

Enfoque Manipulación previa Comprueba los artefactos Carga operativa
Espejo de cuerpo entero Copia todo el contenido de PyPI en el equipo local No Alto: el almacenamiento crece de forma continua
Proxy de paso Recupera y almacena en caché según sea necesario No De bajo a moderado
Lista de permitidos seleccionada Solo se resuelven los paquetes aprobados Solo lo que haya revisado una persona Revisión exhaustiva y continua
Registro gestionado y verificado (p. ej., SecureChain) Se revisa y se repara antes de servir Sí Bajo, subcontratado

Duplicación completa con Bandersnatch es la solución tradicional y, con diferencia, la que más espacio de almacenamiento consume. En el momento de escribir este artículo, todo PyPI ocupa unos 44 TB en archivos de lanzamiento, según la página de estadísticas de PyPI, y esa cifra no hace más que crecer. El almacenamiento es la parte fácil; lo que realmente supone un coste es mantener la sincronización en buen estado con tal volumen de datos.

Los proxies «pull-through» son la opción más habitual. devpi, proxpi, Nexus Repository, Artifactory, AWS CodeArtifact, Azure Artifacts, Google Artifact Registry: productos diferentes, pero con la misma arquitectura subyacente. La función de proxy y caché resuelve bien la disponibilidad y el almacenamiento en caché, pero no hace nada para determinar si un artefacto de origen es fiable, ya que esa nunca ha sido su función.

Las listas de permitidos seleccionadas son uno de los controles más eficaces que un equipo puede aplicar internamente sin necesidad de crear un proceso de verificación completo. También son las que fallan de forma silenciosa, ya que la cola de revisión se convierte en la tarea menos apreciada por alguien y las aprobaciones empiezan a realizarse por lotes para desbloquear un lanzamiento.

Los registros gestionados y verificados se basan en la verificación, más que en la disponibilidad. SecureChain para Python funciona así. Los paquetes se vuelven a compilar a partir del código fuente y se analizan antes de que estén disponibles para su instalación, en lugar de almacenarse en caché a posteriori y analizarse más tarde.

Antes de elegir un método, ejecuta el comando `pip download -r requirements.txt` y cuenta los paquetes distintos que aparecen en el resultado, incluidos los transitivos. Comparar el tamaño de una lista de paquetes permitidos seleccionada con tus dependencia directa , puede subestimar gravemente la carga de revisión, ya que el árbol transitivo es donde reside el volumen.

La duplicación no es lo mismo que la verificación

Hay tres cuestiones distintas que puede resolver una configuración de empaquetado en Python, y es importante diferenciarlas porque los productos difuminan la línea divisoria.

¿Ha llegado este artefacto intacto? Los hash lo confirman, y Pip lo respalda perfectamente.

¿Procede este objeto realmente del lugar que se afirma? La procedencia y la firma dan respuesta a esa pregunta, en parte, cuando intervienen los editores.

¿Este artefacto coincide con el código fuente a partir del cual, según afirma, se ha compilado? Casi nada en la cadena de herramientas predeterminada da respuesta a esa pregunta.

La tercera pregunta es la que detecta las ruedas manipuladas. Una Wheel es un resultado de la compilación, y nada en el sistema de paquetes exige que se corresponda con el repositorio al que enlaza el paquete. Si se compromete la cuenta de un mantenedor, se puede generar un «wheel» cuyo hash sea consistente, se instale sin problemas y contenga código que el repositorio nunca haya visto.

pip-compile –generate-hashes requirements.in
pip install –require-hashes -r requirements.txt

Activa el modo de comprobación de hash independientemente de lo demás que hagas. Obliga a que todas las dependencias, incluidas las transitivas, se fijen con un hash, lo que impide la sustitución silenciosa y hace que las instalaciones sean repetibles. Sin embargo, no confirma en absoluto si el artefacto fijado fue legítimo en algún momento. Simplemente fija lo que se resolvió la primera vez.

Resolver la tercera cuestión implica establecer un vínculo fiable entre el código fuente y el artefacto instalable, lo que en la práctica significa recompilar a partir del código fuente verificado en un entorno controlado e instalar esa compilación en lugar de un binario opaco procedente del desarrollador original. Se trata de un trabajo de ingeniería deliberado, no de un ajuste de configuración.

«Trusted Publishing» y lo que reveló «durabletask»

Publicación de confianza sustituye los tokens de la API de PyPI de larga duración por credenciales de corta duración emitidas a través de OIDC para un flujo de trabajo de CI específico. El token que genera caduca en 15 minutos, por lo que no hay ninguna credencial de larga duración almacenada en un repositorio de secretos a la espera de ser robada.

El SDK de DurableTask de Microsoft no tenía esta función activada cuando la campaña TeamPCP distribuyó tres versiones maliciosas a PyPI el 19 de mayo de 2026. Las versiones 1.4.1 a 1.4.3 se publicaron en un intervalo de 35 minutos, subidas directamente a PyPI con un token de API de larga duración robado que se encontraba en los secretos de GitHub Actions del proyecto. Sin etiquetas que coincidieran, sin que se hubiera ejecutado ningún flujo de trabajo de publicación, nada en el historial del repositorio que explicara cómo llegaron allí esas versiones. Es precisamente esta vía de robo de tokens la que Trusted Publishing pretende cerrar.

Telnyx y LiteLLM ya habían aprendido esa lección unas ocho semanas antes, y por las malas. A ambas se les subieron versiones maliciosas a PyPI en marzo de 2026 sin etiquetas correspondientes en sus repositorios, y ambas se pasaron a Trusted Publishing posteriormente, tal y como documenta el propio informe de incidentes de PyPI. durabletask aún no lo había adoptado cuando TeamPCP volvió a la carga en mayo. A fecha de su última versión en el momento de redactar este artículo, la 1.9.0 del 30 de julio de 2026, PyPI sigue mostrando los archivos como publicados sin Trusted Publishing.

Esa laguna es la parte más relevante de la historia, más que cualquier incidente concreto por sí solo. La solución lleva años existiendo. Dos incidentes de seguridad muy sonados impulsaron a dos proyectos a adoptarla. Semanas más tarde, la misma campaña comprometió un tercer proyecto mediante otra credencial de publicación robada.

Trusted Publishing es realmente eficaz contra el método de robo de credenciales, pero tiene dos limitaciones que conviene dejar claras. Protege los paquetes que publicas, no los que utilizas, por lo que activarlo no tiene ningún efecto sobre las dependencias que ya se encuentran en tu árbol. Además, depende de que todos los responsables de mantenimiento que están por encima de ti en la cadena de desarrollo tomen la misma decisión, algo con lo que no puedes contar.

Controlar Protege No protege
Publicaciones de confianza Paquetes que publicas Cualquier cosa que instales
Fijación de hash En contra de la sustitución tras la primera resolución Frente a una primera resolución que se ha visto comprometida
Repositorio privado Origen, disponibilidad y política de los paquetes controlados Integridad de los artefactos
Cribado a nivel de registro Artefactos antes de que se resuelvan Código que has escrito tú mismo

Si tu organización publica paquetes, activa la función «Publicación de confianza» esta semana. Se trata de un pequeño cambio de configuración que bloquea toda una vía de ataque.

Reforzar la seguridad del lado del cliente

La arquitectura del repositorio acapara toda la atención. La configuración del cliente es donde realmente se aplica la normativa, y suele ser inconsistente.

Hay cuatro aspectos que conviene comprobar en todos los entornos en los que se ejecuta pip. En primer lugar, que haya una única URL de índice en el archivo pip.conf a nivel del sistema, sin ninguna URL de índice adicional en ningún sitio. En segundo lugar, que las variables PIP_INDEX_URL y PIP_EXTRA_INDEX_URL no estén definidas en las imágenes base ni en la configuración de CI. En tercer lugar, que el modo de comprobación de hash esté habilitado en CI, incluso si los desarrolladores lo omiten a nivel local. En cuarto lugar, el tráfico de salida de red de los agentes de compilación debe estar restringido, de modo que un cliente mal configurado no pueda acceder en absoluto al PyPI público.

Es precisamente esto último lo que convierte una política en una medida efectiva. Se producen desviaciones en la configuración, se heredan las imágenes base y siempre hay alguien que activa una opción para desbloquear una implementación. Una política de salida que permita a los agentes de compilación acceder únicamente al repositorio privado autorizado detecta todo eso sin depender de la disciplina de nadie. Bloquear un único nombre de host no es suficiente, ya que el índice y el servidor de archivos al que apunta son independientes.

Elegir un enfoque

Adapta el enfoque a lo que realmente hay que resolver, no a lo que sea más rápido de poner en marcha.

Un proxy de paso directo es la opción adecuada cuando el objetivo es la disponibilidad y el almacenamiento en caché. Es una buena infraestructura, pero deja de ser útil en el momento en que se la trata como un límite de seguridad. Una lista de permitidos cuidadosamente seleccionada añade un punto de control de políticas que una auditoría puede señalar, siempre y cuando alguien evite que la cola de revisión se convierta en un mero trámite. La duplicación completa rara vez justifica su coste de almacenamiento fuera de entornos aislados físicamente. Ninguna de las tres opciones garantiza de por sí la integridad de los artefactos, y una lista de permitidos cuidadosamente seleccionada solo ofrece tanta garantía como la que realmente establezca su proceso de revisión.

Si la integridad de los artefactos entra realmente dentro del alcance, y no solo la disponibilidad o el registro de auditoría, lo que necesitas es una capa de verificación que reconstruya los paquetes a partir de una fuente de confianza y los filtre antes de que se resuelvan. Un registro gestionado y verificado te ofrece eso sin que tengas que crear y gestionar el proceso, lo cual, si se hace internamente, supone un compromiso de ingeniería de varios trimestres, más que un sprint.

Dónde encaja SecureChain para Python

Volvamos a las tres preguntas anteriores: «intacto» y «de donde dice proceder» coinciden con su origen. La mayoría de las configuraciones de repositorios privados se quedan en la primera. SecureChain está diseñado para responder a las tres. Cada paquete se recompila a partir del código fuente en entornos reforzados y se somete a un proceso de verificación antes de que esté disponible para su instalación, acompañado de datos de procedencia SLSA de nivel 3, SBOM y VEX, y con la aplicación de parches que continúa incluso después de que el responsable del mantenimiento deje el proyecto.

Si quieres ver cómo se compara con tu propio árbol de dependencias, pregunta a nuestros expertos sobre SecureChain para Python y cuál es el enfoque más adecuado para tu pila de tecnologías.

PREGUNTAS FRECUENTES

¿Qué es un repositorio privado de PyPI?

Un índice de paquetes de Python alojado internamente que proporciona paquetes a tus desarrolladores y sistemas de compilación, en lugar de redirigirlos directamente al PyPI público. Puede actuar como espejo del repositorio original, como proxy y almacenar en caché bajo demanda, ofrecer únicamente un conjunto de paquetes aprobados o filtrar los paquetes antes de servirlos.

¿Un repositorio privado de PyPI hace que las instalaciones sean más seguras?

Solo si verifica los paquetes. Un espejo o una caché de «pull-through» sirve todo lo que publique la fuente original, incluidas las versiones maliciosas. La ventaja en materia de seguridad proviene de los filtros o la selección que se aplican posteriormente, no del propio repositorio.

¿Cuál es la diferencia entre «index-url» y «extra-index-url»?

–index-url sustituye el índice predeterminado, por lo que solo se consulta tu repositorio. –extra-index-url añade otro índice, y pip busca en todos los índices configurados sin clasificarlos, lo que permite que un paquete público malicioso pueda imponerse en la resolución y provocar un ataque de confusión de dependencias.

¿Cómo puedo evitar la confusión de dependencias con pip?

Utiliza exclusivamente –index-url, distribuye los paquetes internos y de origen a través de ese único índice, elimina la variable PIP_EXTRA_INDEX_URL en todas partes y restringe el tráfico saliente de los agentes de compilación para que solo se pueda acceder al repositorio autorizado.

¿Debería poner en marcha un mirror completo de PyPI?

Rara vez. Una réplica completa implica almacenar aproximadamente 44 TB, cifra que no deja de crecer, y no ofrece ninguna ventaja en cuanto a la verificación frente a un proxy de «pull-through». La réplica completa tiene más sentido en entornos aislados físicamente.

¿Qué es «Trusted Publishing» en PyPI?

Un mecanismo de publicación basado en OIDC que emite credenciales de corta duración para un flujo de trabajo específico de CI, en lugar de recurrir a tokens de API de larga duración. Protege los paquetes que publicas. No tiene ningún efecto sobre los paquetes que instalas.

 

Resumen
Opciones de repositorios privados de PyPI y las limitaciones de cada uno de ellos
Nombre del artículo
Opciones de repositorios privados de PyPI y las limitaciones de cada uno de ellos
Descripción
Un repositorio privado de PyPI controla la procedencia de los paquetes, pero no si son seguros. Compara los servidores espejo, los proxies y los registros seleccionados.
Autor
Nombre del editor
TuxCare
Logotipo de la editorial

Únase a más de 4.500 profesionales de Linux y el código abierto.

2 veces al mes. Sin spam.

Índice
Obtenga las respuestas de seguridad de código abierto que necesita

Únase a más de 4.500 profesionales de Linux y el código abierto.

2 veces al mes. Sin spam.