Y cómo blindar su cadena de suministro antes de que la próxima release envenenada llegue a tu pip install.
Actualización (27 de octubre de 2026). LiteLLM publicó el 23 de octubre su análisis de causa raíz definitivo del incidente de la 1.107.0, y corrige su hipótesis inicial: el primer eslabón no fueron los logs de un gateway, sino una clave maestra filtrada en un repositorio público en mayo y nunca revocada. Hemos actualizado la sección 2.3 y los controles afectados. Sobre la vulnerabilidad cuya divulgación LiteLLM había programado para el 27 de octubre: a las 21:00 (hora peninsular) de ese día aún no había ningún aviso de seguridad publicado que la identifique. Ese mismo día salieron las versiones de parche 1.103.10, 1.104.8, 1.105.6 y 1.106.5 con un cambio marcado como corrección de seguridad en los logs de peticiones rechazadas (PR #48599). LiteLLM no lo ha vinculado públicamente al embargo, así que no lo damos por hecho.El 21 de octubre de 2026, alguien ejecutó pip install litellm sin fijar versión, o arrancó un contenedor con la etiqueta latest. No importó la librería ni levantó el proxy. Le bastó con que Python arrancara en ese entorno para que un recolector de credenciales empezara a leer claves de proveedores de modelos, credenciales de la nube, tokens de GitHub, configuración de herramientas de IA y servidores MCP, y las enviara cifradas a un dominio que imitaba al oficial.
No es un supuesto. Es lo que describen los propios mantenedores de LiteLLM y el equipo de Microsoft Security Research sobre la versión 1.107.0, publicada con malware en PyPI y en las imágenes oficiales de contenedor. Ocho días antes, Check Point Research había publicado dos vulnerabilidades graves en el mismo gateway. Y en marzo, el mismo proyecto ya había sufrido otro compromiso de su cadena de publicación.
Este es el segundo post de nuestra serie sobre buenas prácticas en el desarrollo de productos de IA. El primero trató la seguridad de los agentes frente a la prompt injection; hoy bajamos una capa, a la infraestructura que casi nadie revisa porque "solo es un proxy". No escribimos esto para señalar a LiteLLM: es un proyecto útil, ha respondido con transparencia y lo que le ha pasado puede pasarle a cualquier dependencia de tu stack. Lo usamos porque es el caso más claro y reciente de un patrón que afecta a todos los equipos que llevan IA a producción.
1. Qué es un gateway de LLM y por qué concentra tanto riesgo
Un gateway de LLM se sitúa entre tus aplicaciones y los proveedores de modelos. En lugar de que cada servicio llame directamente a OpenAI, Anthropic o Google, todas las peticiones pasan por una capa que centraliza las credenciales, el enrutado entre modelos, los fallbacks, los límites de uso, el registro de peticiones y el control de gasto. LiteLLM, Portkey, las pasarelas de los proveedores cloud o una implementación propia cumplen ese papel.
Es una buena idea de arquitectura: evita repartir API keys por diez servicios y te da un único punto de observabilidad y control de costes. Pero ese punto único tiene una consecuencia que conviene decir en voz alta: el gateway es, por diseño, el sitio donde están todas las llaves. Microsoft lo resume así en su análisis del incidente: en esa posición, el gateway puede tener acceso a las API keys de los proveedores, cadenas de conexión a base de datos, identidades de carga de trabajo, configuración de enrutado y datos de políticas por cliente.
A eso se suman dos propiedades que lo hacen más delicado que un microservicio cualquiera:
- Ve todo el tráfico. Prompts, respuestas, documentos recuperados por RAG, datos personales que el usuario pega en un chat. Si registra cuerpos completos, sus logs son una copia de todo eso.
- La misma dependencia vive en muchos sitios. El paquete no solo corre en el servidor del proxy: también en portátiles de desarrollo, agentes de CI, entornos de test y notebooks. Microsoft subraya precisamente que eso amplió el conjunto de entornos expuestos.
Con eso en mente, veamos lo que ha pasado en las últimas semanas.
2. Tres incidentes en un mes, tres lecciones distintas
2.1 13 de octubre: control de acceso roto en el propio gateway
Check Point Research publicó dos vulnerabilidades en LiteLLM (analizadas sobre la versión 1.77.7 y corregidas a partir de la 1.83.9):
- CVE-2026-35029: el endpoint administrativo
POST /config/updateno comprobaba el rol del usuario. Cualquier usuario autenticado, incluido uno de solo lectura, podía modificar la configuración en caliente. A partir de ahí, los investigadores demostraron lectura arbitraria de ficheros, toma de control de la cuenta de administrador y ejecución remota de código cargando un módulo Python desde un bucket. - CVE-2026-59823: una SSRF en la función de credenciales del lado del cliente permitía redirigir las llamadas del gateway a un servidor del atacante y capturar las API keys del proveedor.
El detalle técnico más instructivo de la primera es cómo se colaba la petición: la lista de rutas permitidas para sesiones de la interfaz web incluía /config (probablemente pensada para leer la configuración), y la comprobación usaba startswith. /config/update empieza por /config, así que pasaba. Check Point lo resume en su conclusión: no son clases de ataque nuevas, sino fallos de control de acceso y validación de entradas bien conocidos.
Lección: el gateway es software de backend con un panel de administración, y se le aplica todo lo que ya sabéis de seguridad web. No lo expongáis a internet "porque es interno", y no deis cuentas de usuario en él a quien no las necesita.
2.2 20 de octubre: el servidor de inferencia también es superficie
El mismo mes, vLLM publicó el aviso GHSA-6757-hggm-hp2w: en despliegues con la caché multimodal en memoria compartida activada (--mm-processor-cache-type shm), un usuario autenticado de la API de inferencia podía forjar una referencia que acababa en un pickle.loads() en el worker, es decir, ejecución de código arbitrario. Afecta a versiones hasta la 0.25.1 y está corregido en la 0.32.0.
Lección: si autoalojáis modelos, "autenticado" no equivale a "de confianza". Una API key de inferencia que entregáis a un cliente o a otro equipo puede ser suficiente para ejecutar código en vuestras GPUs si no estáis al día con los parches.
2.3 21 de octubre: la release envenenada
Este es el incidente que da título al post. Según la página de incidente de LiteLLM:
litellm==1.107.0estuvo publicada en PyPI con malware durante unas dos horas (de 11:39 a 13:23 UTC).- Las etiquetas Docker
v1.107.0,latest,main-latestymain-stableapuntaron a una imagen comprometida entre las 11:46 y las 14:20 UTC. - El tag de código fuente oficial no contenía los ficheros maliciosos: los artefactos se construyeron desde un repositorio del atacante con un nombre casi idéntico (
Berri-AI/litellmen lugar deBerriAI/litellm).
Lo más incómodo es cómo llegó ahí. Tras el incidente de marzo, el equipo había endurecido su pipeline: releases desde un repositorio privado, publicación con Trusted Publishers, etiquetas Docker inmutables e imágenes firmadas. Su análisis de causa raíz, publicado el 23 de octubre y que ellos mismos presentan como la fuente canónica, reconstruye la cadena completa:
- 27 de mayo. Se sube por error a un repositorio público un fichero de configuración con la clave maestra de un despliegue de LiteLLM Agent Platform, una herramienta propia para orquestar agentes. El commit se sobrescribió, pero la clave maestra nunca se revocó.
- 10 de octubre. El atacante usa esa clave contra la API de la plataforma. Una petición que lista los agentes configurados devuelve el token OAuth de la CLI de GitHub de uno de los dos propietarios de la organización: era el token con el que los agentes abrían PRs, personal, sin acotar y no asociado a una cuenta de servicio.
- 10 y 12 de octubre. Con ese token, el atacante sube ramas desechables cuyos workflows de CI envían los secretos de los repositorios a un endpoint externo. Así obtiene los tokens de otros dos mantenedores, guardados como secretos de CI, y borra después esas ejecuciones.
- 21 de octubre. Con el token del propietario se salta la protección de ramas y apunta el workflow de release al repositorio clonado (
Berri-AI/litellm). Esa misma semana se había relajado temporalmente la aprobación de producción para permitir backports más frecuentes: bastaban dos ingenieros cualesquiera. Con el segundo token aprueba la publicación y con el tercero borra los registros de las ejecuciones. El pipeline legítimo firmó y publicó los artefactos maliciosos.
La primera hipótesis, publicada durante el incidente, apuntaba a tokens que un asistente de programación había incluido en respuestas registradas en los logs de un despliegue interno de LiteLLM expuesto a internet. El análisis final la descarta para este caso: los logs de peticiones muestran que el primer token salió de otro servicio, la API de la plataforma de agentes.
Es decir: una clave filtrada cinco meses antes y nunca revocada, en una plataforma de agentes que guardaba y devolvía un token de GitHub con permisos de propietario, fue el primer eslabón para comprometer la cadena de publicación del propio gateway. Ninguna pieza es exótica por separado; el problema fue la suma.
El análisis de Microsoft añade el detalle técnico que explica por qué el radio de impacto fue tan grande. El paquete instalaba cook.pth, cook.py y sitecustomize.py fuera del directorio de la librería. Python procesa los ficheros .pth y sitecustomize en cada arranque del intérprete, así que no hacía falta importar LiteLLM: en un caso observado en Windows, una invocación rutinaria de Python para instalar una dependencia ajena activó el recolector. Ese recolector buscaba configuración de asistentes de IA, IDEs e integraciones MCP, credenciales de AWS, Azure y Google Cloud (incluidos los endpoints de metadatos), tokens de Kubernetes, claves SSH, datos de navegadores, variables de entorno de otros procesos y registros de credenciales de la base de datos del proxy. Además, en sistemas Unix dejaba un cargador capaz de descargar y ejecutar código adicional desde un servidor controlado por el atacante.
El incidente de marzo siguió un patrón parecido por otra puerta. Las versiones 1.82.7 y 1.82.8 se publicaron con un ladrón de credenciales, y la investigación apunta a que el origen fue Trivy, el escáner de seguridad que usaban en CI. Según JFrog, el script del pipeline instalaba Trivy con apt-get install trivy sin fijar versión ni verificar checksum, así que descargó automáticamente una release maliciosa del escáner, que se llevó los tokens de publicación. Un detalle relevante: LiteLLM indicó que los usuarios de su imagen Docker oficial no se vieron afectados en marzo porque esa vía fijaba dependencias en requirements.txt.
3. Lo que estos incidentes enseñan (más allá de LiteLLM)
Cuatro ideas que aplicamos en cualquier revisión de arquitectura de un producto de IA:
- Firmar no es verificar el origen. En octubre, los artefactos estaban firmados por el pipeline legítimo. La firma demuestra quién construyó el artefacto, no desde qué código. Hay que verificar también la procedencia (repositorio y commit de origen) y, sobre todo, no aceptar versiones nuevas a ciegas.
- Las etiquetas mutables son una puerta abierta.
latestymain-stableapuntaron durante horas a una imagen maliciosa. Quien desplegaba por digest o tenía fijada una versión anterior no recibió nada. Las versiones fijadas con hash no evitan que un proyecto sea comprometido, pero sí que el compromiso llegue solo a tu producción. - Las herramientas de seguridad y de IA son objetivos preferentes. Endor Labs señala que la campaña de marzo apuntó deliberadamente a herramientas cercanas a seguridad (Trivy, KICS) y luego a un proxy de LLM, porque corren en entornos llenos de credenciales. Un gateway de IA, un escáner en CI o un servidor MCP en el portátil del desarrollador son exactamente ese tipo de entorno.
- Las credenciales de los agentes son credenciales de producción. El primer token robado en octubre no estaba en un servidor de producción, sino en la configuración de una plataforma interna de agentes, y era un token personal con permisos de propietario. Si un agente necesita abrir PRs, dadle una cuenta de servicio o una GitHub App con permisos mínimos y credenciales de corta duración, y no dejéis que ninguna API devuelva secretos en claro. Es el mismo mínimo privilegio que tratamos en el primer post de la serie, aplicado a la infraestructura.
OWASP lo recoge como LLM03:2025 Supply Chain en su Top 10 para aplicaciones LLM: además de los riesgos clásicos de paquetes de terceros, la cadena de suministro en IA incluye modelos preentrenados, adaptadores LoRA, plataformas de despliegue y los términos de uso de los proveedores. Entre sus mitigaciones propone mantener un inventario firmado y actualizado de componentes (SBOM) y evaluar los AI BOM o ML-BOM empezando por CycloneDX.
4. Siete controles que aplicaríamos esta semana
Ordenados de más barato a más costoso. Los cuatro primeros se hacen en una tarde.
4.1 Versiones fijadas con hash, también en CI
Un requirements.txt con litellm>=1.80 o un pip install litellm en un Dockerfile significa "instala lo último que haya publicado quien controle esa cuenta hoy". Fijad versión y hash de cada artefacto. Con pip, el modo de verificación de hashes es todo o nada: si una sola línea lleva --hash, se exige para todas.
# Generar requirements con hashes (uv o pip-tools) uv pip compile requirements.in --generate-hashes -o requirements.txt # Instalar exigiendo hashes y sin compilar paquetes desde código fuente pip install --require-hashes --only-binary :all: -r requirements.txt
Si usáis uv con proyectos, uv.lock ya registra los hashes; lo importante es que CI y los Dockerfiles instalen desde el lockfile y fallen si no está sincronizado:
uv sync --locked # falla si uv.lock no coincide con pyproject.toml
Y lo mismo para las herramientas del pipeline: el compromiso de marzo entró por un escáner instalado sin versión. Las Actions de GitHub, fijadas al SHA completo del commit, como recomienda la propia guía de seguridad de GitHub Actions:
# Mal: la etiqueta v4 la puede mover quien controle el repositorio - uses: actions/checkout@v4 # Bien: SHA completo del commit, con la versión como comentario - uses: actions/checkout@<sha-completo-de-40-caracteres> # v4.x.y
4.2 Imágenes por digest, nunca por etiqueta flotante
# Mal FROM ghcr.io/berriai/litellm:main-stable # Bien: el digest identifica contenido inmutable FROM ghcr.io/berriai/litellm@sha256:<digest-verificado>
Lo mismo en los manifiestos de Kubernetes y en Helm. Microsoft lo incluye explícitamente en sus recomendaciones: los digests son preferibles a las etiquetas mutables porque identifican contenido inmutable.
4.3 Cuarentena para versiones nuevas
Los dos compromisos de LiteLLM estuvieron disponibles unas horas (unos 40 minutos en marzo; unas dos horas en PyPI en octubre). Un retraso de unos días antes de aceptar una versión nueva habría evitado instalar cualquiera de los dos. Hay herramientas que lo hacen nativo:
# pyproject.toml — uv solo considera artefactos subidos hace más de 7 días # (las duraciones relativas requieren una versión reciente de uv) [tool.uv] exclude-newer = "7 days"
# .github/dependabot.yml — no proponer versiones con menos de 7 días
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
cooldown:
default-days: 7
semver-major-days: 14Dependabot aplica por defecto un cooldown de 3 días a las actualizaciones de versión, y esa espera no afecta a las actualizaciones de seguridad, que es justo lo que queréis: los parches críticos (como el 1.83.9 de LiteLLM) entran rápido y el resto espera a que la comunidad haya tenido tiempo de detectar algo raro.
4.4 Vigilar los ganchos de arranque de Python
El malware de octubre (y la versión 1.82.8 de marzo) usó ficheros .pth con líneas import, que Python ejecuta al arrancar. Python 3.15 ya ha marcado esas líneas como obsoletas, pero se siguen ejecutando y seguirán existiendo en los entornos actuales durante años. Un control barato es fijar una línea base de qué ficheros .pth ejecutables y sitecustomize.py existen en vuestras imágenes, y hacer fallar el build si aparece uno nuevo:
#!/usr/bin/env sh
# check-startup-hooks.sh — no invoca Python a propósito: si el entorno
# estuviera comprometido, arrancar el intérprete ejecutaría el payload.
SITE_DIRS="${SITE_DIRS:-/usr/local/lib /opt/venv/lib}"
found=$(
find $SITE_DIRS \( -name '*.pth' -o -name 'sitecustomize.py' -o -name 'usercustomize.py' \) 2>/dev/null |
while read -r f; do
case "$f" in
*.pth) grep -qE '^import[[:space:]]' "$f" && echo "$f" ;;
*) echo "$f" ;;
esac
done | sort
)
# allowlist revisada a mano (algunos paquetes legítimos usan .pth ejecutables)
if ! echo "$found" | diff -u startup-hooks.allowlist - ; then
echo "Nuevo gancho de arranque de Python detectado. Revisar antes de desplegar." >&2
exit 1
fiNo sustituye a un escáner de malware, pero convierte la técnica concreta que se ha usado dos veces este año en una alerta de CI.
4.5 Aislar el gateway y cerrar su salida a internet
El gateway necesita hablar con un puñado de hosts conocidos (los endpoints de vuestros proveedores de modelos, la base de datos, el sistema de observabilidad). No necesita hablar con nada más. Si el recolector de octubre no hubiera podido hacer su POST al dominio del atacante, el robo de credenciales no habría salido del host. Microsoft recomienda que estaciones de trabajo, agentes de build, entornos de test y hosts de gateways de IA no tengan acceso HTTP de salida sin restricciones, sino que pasen por controles de egress con allowlist.
En la práctica:
- El panel de administración y los endpoints de gestión (
/config,/key,/user...) no se publican a internet. Acceso por VPN o red privada, y solo para los administradores. - Salida a internet solo a través de un proxy de egress con allowlist de dominios, aplicada a nivel de red (las
NetworkPolicyde Kubernetes trabajan con IPs y selectores, no con dominios, así que para filtrar por nombre de host hace falta un proxy de salida o un CNI que lo soporte). - Bloquear el acceso al endpoint de metadatos de la nube (
169.254.169.254) desde el contenedor del gateway si no lo necesita, o exigir IMDSv2 en AWS. El recolector de octubre intentaba obtener credenciales de ahí.
4.6 Logs del gateway sin secretos y con retención corta
La primera hipótesis de LiteLLM sobre octubre apuntaba a tokens que habían acabado en los logs de su gateway. El análisis final la descartó para este caso, pero el riesgo está documentado: en marzo, el propio proyecto publicó un incidente en el que las respuestas de guardarraíles personalizados podían llevar cabeceras Authorization en claro a los spend logs y a las trazas de OpenTelemetry. Y el 27 de octubre publicó una corrección de seguridad para que los logs de peticiones rechazadas se construyan a partir de una lista cerrada de campos: hasta entonces se generaban directamente desde el cuerpo de la petición, y quien llamaba podía influir en a dónde iba ese log. Registrar cuerpos completos de peticiones y respuestas es tentador para depurar, pero convierte vuestros logs en un almacén de prompts, datos personales y, a veces, credenciales.
- Desactivad el registro de cuerpos completos por defecto; activadlo solo de forma temporal y acotada.
- Redactad patrones de secretos antes de persistir (tokens
ghp_/gho_,sk-,AKIA, JWTs, cabecerasAuthorization). - Retención corta y acceso restringido a los logs. Si vuestra herramienta de observabilidad de LLM guarda trazas completas, aplicadle la misma política (de la observabilidad hablamos en este post).
Bonus de diseño: si escribís vuestro propio control de acceso (en el gateway o en cualquier API), comparad rutas y permisos de forma exacta o por patrones explícitos, nunca por prefijo:
# Mal: "/config/update".startswith("/config") es True
READ_ONLY_UI_ROUTES = ["/config", "/spend", "/key/info"]
def allowed(route: str) -> bool:
return any(route.startswith(r) for r in READ_ONLY_UI_ROUTES)
# Mejor: rutas exactas y método explícito; todo lo administrativo exige rol
READ_ONLY_UI_ROUTES = {("GET", "/config"), ("GET", "/spend"), ("GET", "/key/info")}
def allowed(method: str, route: str, role: str) -> bool:
if (method, route) in READ_ONLY_UI_ROUTES:
return True
return role == "admin"4.7 Inventario (AIBOM) y plan de rotación ensayado
Cuando sale un aviso como el de octubre, la primera pregunta es "¿estamos afectados?". Si la respuesta requiere preguntar en Slack a cada equipo, llegáis tarde. Microsoft pide localizar la versión comprometida en entornos instalados, cachés de uv y pip, mirrors internos, lockfiles, artefactos de build e imágenes. Eso solo es rápido si tenéis inventario.
Un inventario mínimo para un producto de IA debería incluir, por servicio y entorno:
| Componente | Qué registrar |
|---|---|
| Gateway de LLM | Versión exacta, digest de imagen, dónde corre, qué secretos tiene montados |
| SDKs y frameworks (LLM, agentes, RAG) | Versión y hash desde el lockfile |
| Servidores de inferencia propios | Versión, flags relevantes (p. ej. cachés compartidas), exposición de red |
| Modelos | Proveedor e identificador de versión; si son pesos propios o de terceros, origen y hash |
| Servidores MCP y herramientas | Origen, versión fijada, permisos y credenciales que usan |
| Herramientas de CI | Versión fijada de escáneres, Actions por SHA |
CycloneDX tiene un formato específico para esto (ML-BOM), y OWASP lo sugiere como punto de partida. Pero no esperéis a la herramienta perfecta: una tabla generada desde vuestros lockfiles y manifiestos ya responde a casi todo lo que necesitáis saber cuando preguntáis "¿dónde tenemos esto?".
La otra mitad es la rotación, y empieza antes del incidente: la cadena de octubre arrancó con una clave expuesta en mayo cuyo commit se sobrescribió pero que nunca se revocó. Borrar un secreto del historial no lo invalida; hay que revocarlo y comprobar que la revocación ha surtido efecto. Para la respuesta a un incidente, la guía de LiteLLM para afectados pide rotar el rol IAM de la instancia, todas las claves de proveedor guardadas en la base de datos del proxy, las claves maestras y virtuales, la contraseña de la base de datos y cualquier secreto del entorno de cualquier proceso del host. Microsoft añade revocar sesiones activas, porque rotar una contraseña no invalida automáticamente todos los tokens emitidos. Si nunca habéis rotado vuestras claves de proveedor de modelos, haced el ensayo ahora, con calma, y no a las tres de la tarde de un martes con un incidente abierto. Y preferid identidades de carga de trabajo con credenciales de corta duración frente a claves estáticas siempre que el proveedor lo permita.
5. Dos errores que deshacen todo lo anterior
Error 1: "usamos la imagen oficial, así que estamos a salvo". En octubre, la imagen oficial con la etiqueta main-stable era la comprometida. Oficial y firmada no significa revisada por vosotros. Lo que os protege es fijar por digest, retrasar la adopción de versiones nuevas y limitar lo que el contenedor puede alcanzar si algo sale mal.
Error 2 (el contrapeso): congelar dependencias y no actualizar nunca. Las vulnerabilidades de Check Point estaban corregidas en la 1.83.9; quien siguiera en una versión anterior por "estabilidad" tenía un gateway que cualquier usuario autenticado podía tomar. Fijar versiones no es lo mismo que no actualizar: es actualizar de forma deliberada, con una cuarentena para lo nuevo y una vía rápida para los parches de seguridad.
Checklist: gateway de LLM y cadena de suministro antes de ir a producción
Dependencias y artefactos
- [ ] Todas las dependencias Python se instalan desde un lockfile con hashes (
--require-hashesouv sync --locked), también en CI y Dockerfiles. - [ ] Ninguna imagen se despliega por etiqueta flotante (
latest,main-stable); todas por digest. - [ ] Las GitHub Actions están fijadas a SHA completo y las herramientas de CI (escáneres incluidos) tienen versión fijada.
- [ ] Hay cuarentena para versiones nuevas (
exclude-newer,cooldownde Dependabot o equivalente) y una vía rápida documentada para parches de seguridad. - [ ] El build falla si aparece un
.pthejecutable o unsitecustomize.pyno incluido en la allowlist.
Gateway e inferencia
- [ ] El panel y los endpoints de administración del gateway no son accesibles desde internet.
- [ ] Las cuentas del gateway siguen mínimo privilegio y se revisan periódicamente (sin usuarios "de prueba" olvidados).
- [ ] El gateway y los servidores de inferencia están en una versión sin vulnerabilidades conocidas (comprobado contra los avisos del proyecto).
- [ ] La salida a internet del gateway pasa por un egress con allowlist, y el endpoint de metadatos de la nube está bloqueado o restringido.
- [ ] Cada aplicación usa su propia clave virtual con límites de gasto; las claves de proveedor no salen del gateway.
Logs y secretos
- [ ] El gateway no registra cuerpos completos por defecto y redacta secretos antes de persistir.
- [ ] Los logs y trazas de LLM tienen retención corta y acceso restringido.
- [ ] Las credenciales son de corta duración (identidades de carga de trabajo) siempre que sea posible.
- [ ] Cualquier secreto expuesto (en un repositorio, un log o un ticket) se revoca de inmediato, no solo se borra, y se verifica la revocación.
- [ ] Los agentes y los workflows de CI usan cuentas de servicio o GitHub Apps con permisos mínimos, nunca tokens personales de administradores.
Respuesta
- [ ] Existe un inventario (AIBOM) que responde en minutos "¿dónde corre la versión X de este componente?".
- [ ] Hay un runbook de rotación de claves de proveedores de modelos, nube, base de datos y CI, y se ha ensayado al menos una vez.
- [ ] El runbook indica investigar entornos sospechosos desde fuera, sin ejecutar
pythonnipipdentro. - [ ] Alguien del equipo sigue los avisos de seguridad de las dependencias críticas de IA (gateway, servidor de inferencia, SDKs, servidores MCP).
Conclusión
El gateway de LLM es una de las mejores decisiones de arquitectura que puede tomar un producto de IA: centraliza claves, costes y observabilidad. Precisamente por eso es el componente que más merece un tratamiento de infraestructura crítica, no de "proxy que montamos en una tarde".
Lo que han mostrado estas semanas no es que una herramienta concreta sea insegura, sino que la cadena que lleva código de terceros hasta vuestra producción tiene muchos eslabones, y basta uno: una clave filtrada que nadie revocó, un token de propietario en la configuración de un agente, un escáner sin versión fijada, una etiqueta que apunta a otro sitio. Ninguno de los controles de este post es exótico. Son prácticas de ingeniería de software de siempre aplicadas a la parte del stack que más rápido se está montando y que menos se revisa.
La semana que viene seguimos con otra pieza que suele quedarse fuera de las demos. Si tenéis un gateway o un servidor de inferencia en producción y queréis revisar su exposición y su cadena de suministro, estaremos encantados de tener esa conversación. Sin pitch: una llamada de 30 minutos con uno de nuestros ingenieros.
Fuentes
- LiteLLM. Contained: Oct 21, 2026 LiteLLM release v1.107.0 Compromised (21 oct. 2026, actualizado el 23 oct.). https://docs.litellm.ai/blog/security-incident-oct-21-2026
- Microsoft Security Research. Inside the LiteLLM 1.107.0 supply chain compromise: Credential theft through Python startup hooks (23 oct. 2026). https://www.microsoft.com/en-us/security/blog/2026/10/23/inside-the-litellm-1-107-0-supply-chain-compromise-credential-theft-through-python-startup-hooks/
- Check Point Research. When LLM Gateways Go Wrong: Findings from LiteLLM (13 oct. 2026). CVE-2026-35029, CVE-2026-59823. https://research.checkpoint.com/2026/when-llm-gateways-go-wrong-findings-from-litellm/
- Check Point. Check Point Research Discovers High Risk Vulnerabilities in LiteLLM, One of the Most Widely Used LLM Gateways (13 oct. 2026). https://blog.checkpoint.com/security/check-point-research-discovers-high-risk-vulnerabilities-in-litellm-one-of-the-most-widely-used-llm-gateways/
- vLLM. GHSA-6757-hggm-hp2w: Public multimodal SHM handle forgery reaches worker-side pickle.loads() (20 oct. 2026). https://github.com/vllm-project/vllm/security/advisories/GHSA-6757-hggm-hp2w
- LiteLLM. Security Update: Suspected Supply Chain Incident (marzo 2026). https://docs.litellm.ai/blog/security-update-march-2026
- JFrog Security Research. Popular litellm Python package is the latest victim of TeamPCP's ongoing supply chain attack (marzo 2026). https://research.jfrog.com/post/litellm-compromised-teampcp/
- Datadog Security Labs. LiteLLM and Telnyx compromised on PyPI: Tracing the TeamPCP supply chain campaign (2026). https://securitylabs.datadoghq.com/articles/litellm-compromised-pypi-teampcp-supply-chain-campaign/
- Endor Labs. TeamPCP Isn't Done: Threat Actor Behind Trivy and KICS Compromises Now Hits LiteLLM (24 mar. 2026). https://www.endorlabs.com/learn/teampcp-isnt-done
- OWASP Gen AI Security Project. LLM03:2025 Supply Chain. https://genai.owasp.org/llmrisk/llm032025-supply-chain/
- CycloneDX. Machine Learning Bill of Materials (AI/ML-BOM). https://cyclonedx.org/capabilities/mlbom/
- Python documentation. site — Site-specific configuration hook (ficheros
.pth,sitecustomize, obsolescencia de líneasimporten 3.15). https://docs.python.org/3/library/site.html - pip documentation. Secure installs (hash-checking mode,
--require-hashes,--only-binary). https://pip.pypa.io/en/stable/topics/secure-installs/ - uv documentation. Settings: exclude-newer. https://docs.astral.sh/uv/reference/settings/#exclude-newer · Locking and syncing. https://docs.astral.sh/uv/concepts/projects/sync/
- GitHub Docs. Dependabot options reference: cooldown. https://docs.github.com/en/code-security/dependabot/working-with-dependabot/dependabot-options-reference
- GitHub Docs. Secure use reference (fijar Actions a SHA completo). https://docs.github.com/en/actions/reference/security/secure-use
- PyPI Docs. Trusted Publishers. https://docs.pypi.org/trusted-publishers/ · Attestations. https://docs.pypi.org/attestations/
- LiteLLM. The attack chain behind the v1.107.0 LiteLLM release (análisis de causa raíz, 23 oct. 2026). https://docs.litellm.ai/blog/security-incident-oct-21-2026-attack-chain
- LiteLLM. Incident Report: Guardrail logging exposed secret headers in spend logs and traces (18 mar. 2026). https://docs.litellm.ai/blog/guardrail-logging-secret-exposure-incident
- LiteLLM. PR #48599, fix(security)!: build proxy-only failure logs from an allowlist and drop caller guardrail info (27 oct. 2026), incluido en v1.103.10, v1.104.8, v1.105.6 y v1.106.5. https://github.com/BerriAI/litellm/pull/48599 · https://github.com/BerriAI/litellm/releases/tag/v1.106.5