Cloud Security
CI/CD
DevSecOps
GitHub Actions

Seguridad CI/CD: proteger pipelines y secretos

Seguridad del pipeline CI/CD: protege tus builds frente a poisoned pipeline execution, fuga de secretos y runners inseguros con OIDC, SLSA y hardening.

Secra6 de julio de 202611 min de lectura

La seguridad del pipeline CI/CD es el conjunto de controles que impiden que un atacante convierta tu cadena de integración y despliegue continuos en una vía de ejecución de código, robo de credenciales o distribución de artefactos manipulados. El pipeline es un objetivo goloso: tiene acceso al código fuente, a los secretos de despliegue, a los registros de artefactos y, a menudo, a producción. Comprometer un solo workflow puede valer más que comprometer cien estaciones de trabajo. Esta guía es un manual práctico de hardening para 2026, centrado en la lógica de ejecución del pipeline, los secretos y los runners, no en las dependencias que ya cubrimos en la guía de cadena de suministro.

Conviene marcar la diferencia desde el principio. Los ataques a la cadena de suministro de software se centran en las dependencias que consumes: paquetes de npm o PyPI envenenados, typosquatting, dependency confusion. Este artículo va un paso más adentro, hacia la maquinaria que construye y despliega: quién puede modificar un workflow, qué credenciales ve un job y con qué privilegios corre un runner. Es la superficie que el proyecto OWASP Top 10 de riesgos CI/CD codifica de CICD-SEC-1 a CICD-SEC-10.

La superficie de ataque del pipeline CI/CD

Un pipeline moderno es código, no configuración estática. Un fichero en .github/workflows, un .gitlab-ci.yml o un Jenkinsfile describe pasos que se ejecutan de forma automática ante un evento, normalmente un push o una pull request. Esa automatización es justo lo que un atacante quiere secuestrar. La superficie de ataque se divide en cuatro bloques:

  • La definición del pipeline: quién puede editar el workflow, y si un cambio no revisado puede ejecutarse solo.
  • Los secretos: tokens de nube, claves de firma, credenciales de registro y el propio token del sistema CI.
  • El runner: el proceso que ejecuta los pasos, su nivel de privilegio, si es efímero y si está compartido entre proyectos de distinta confianza.
  • Los artefactos: la imagen o el binario que sale al final, y si su procedencia es verificable.

Cierra estos cuatro bloques y habrás eliminado la mayor parte de las rutas de ataque prácticas contra tu cadena de build.

Poisoned Pipeline Execution (PPE): el riesgo número uno

La ejecución envenenada del pipeline (Poisoned Pipeline Execution, o PPE, CICD-SEC-4 en el listado de OWASP) consiste en inyectar comandos maliciosos en la definición del pipeline para que el sistema CI los ejecute con sus propios privilegios. Es la vía más directa hacia los secretos y hacia producción. Se presenta en tres variantes:

  • PPE directo (D-PPE): el atacante modifica el propio fichero de workflow. Si tiene permiso de escritura sobre una rama que dispara builds, añade un paso que exfiltra env a un servidor externo.
  • PPE indirecto (I-PPE): cuando la definición del workflow está protegida, el atacante inyecta comandos en los ficheros que el pipeline invoca, como un Makefile, un script de test o una tarea de npm. El workflow sigue igual, pero ejecuta código controlado por el atacante.
  • PPE público (3PE): el pipeline se dispara desde pull requests de forks externos y ejecuta código no confiable con acceso a secretos.

El caso más famoso en GitHub Actions es el disparador pull_request_target. A diferencia de pull_request, se ejecuta en el contexto del repositorio base con un GITHUB_TOKEN de escritura y acceso a los secretos, pero puede hacer checkout del código de un fork no confiable. Si ese workflow ejecuta el código del fork (por ejemplo npm install con scripts de post-instalación), un atacante externo consigue ejecución con tus secretos. La regla es simple: nunca hagas checkout ni ejecutes código de un fork dentro de un workflow con pull_request_target.

El incidente de tj-actions/changed-files (CVE-2025-30066, marzo de 2025) mostró el alcance real: una GitHub Action muy popular fue comprometida para volcar secretos del runner en los logs de miles de repositorios. La lección no fue solo actualizar, sino dejar de confiar en tags mutables.

Fuga de secretos en el pipeline

La higiene de credenciales (CICD-SEC-6) es donde más equipos tropiezan. Los secretos se filtran de formas sorprendentemente mundanas:

  • Un echo o un set -x que imprime una variable enmascarada en un log. El enmascarado del sistema CI ayuda, pero no cubre transformaciones del valor (base64, troceado).
  • Secretos escritos a disco en el workspace y recogidos luego por un paso o un artefacto.
  • Credenciales de larga duración inyectadas en cada job cuando solo un paso las necesita.

Aplica escaneo de secretos con Gitleaks o TruffleHog en pre-commit y dentro del pipeline, para cortar el secreto antes de que llegue al historial. Reduce el radio de impacto acotando cada secreto al job que lo usa y rotando cualquier credencial que haya podido tocar un log. El error de raíz, sin embargo, casi siempre es el mismo que en tantas malas configuraciones cloud: claves estáticas y de larga vida donde debería haber credenciales efímeras.

Federación OIDC: matar las credenciales de larga duración

La mejor forma de proteger una clave de despliegue es no tener ninguna. La federación OIDC permite que el pipeline intercambie un token de identidad de corta duración, firmado por el proveedor CI, por credenciales temporales del proveedor cloud, sin almacenar ninguna clave permanente.

En GitHub Actions se activa con permissions: id-token: write y la acción aws-actions/configure-aws-credentials, que asume un rol IAM. La pieza crítica está en la política de confianza del rol: el proveedor OIDC es token.actions.githubusercontent.com y la condición debe fijar el claim sub al repositorio y, mejor aún, a un entorno o rama concretos, por ejemplo repo:miorg/mirepo:environment:production. Si dejas el sub abierto con un comodín, cualquier repositorio de la organización (o peor, cualquier repo de GitHub) podría asumir tu rol. Esto conecta de lleno con las rutas de escalada de privilegios en AWS IAM: una condición de confianza laxa es una puerta de escalada.

El mismo patrón existe en GCP (Workload Identity Federation) y en Azure (federated credentials), y GitLab CI emite tokens OIDC vía id_tokens. Migrar de claves estáticas a OIDC elimina de golpe la categoría entera de "secreto de nube filtrado en un log".

Aislamiento de runners

El runner es donde el código se ejecuta de verdad, así que su aislamiento define el radio de impacto de cualquier fallo. Dos principios:

  • Runners efímeros: cada job debe correr en una máquina o contenedor limpio que se destruye al terminar. Un runner persistente acumula credenciales cacheadas, claves SSH y artefactos que el siguiente job (potencialmente malicioso) puede leer.
  • Nunca runners self-hosted en repositorios públicos: la propia documentación de GitHub lo advierte. Un fork puede abrir una PR que ejecute código arbitrario en tu runner self-hosted, y desde ahí pivotar a tu red interna.

Segrega runners por nivel de confianza: los jobs que tocan producción no comparten flota con los que construyen ramas de feature. En GitLab, usa tags para dirigir jobs a runners concretos y evita privileged = true en el executor de Docker salvo que sea imprescindible, porque equivale a root sobre el host. En Kubernetes, el mismo aislamiento se apoya en las prácticas que cubrimos en pentesting de Kubernetes y seguridad del clúster.

SLSA y procedencia verificable

Aunque el pipeline corra limpio, necesitas poder demostrar que el artefacto que se desplegó es el que ese pipeline produjo. El marco SLSA (Supply-chain Levels for Software Artifacts) define niveles de build crecientes: en Build L1 generas procedencia; en L2 la firmas y usas un servicio de build gestionado; en L3 el build es aislado y la procedencia es infalsificable.

En la práctica, esto significa firmar los artefactos con Cosign y Sigstore, emitir atestaciones de procedencia en formato in-toto que registren el commit de origen y el workflow, y verificar esa firma en el despliegue para que un artefacto sin procedencia válida nunca llegue a ejecutarse. Es el mismo control de integridad que un CSPM aplica a la configuración cloud, trasladado al artefacto de build.

Hardening por plataforma

GitHub Actions

Empieza con el GITHUB_TOKEN en modo lectura por defecto (permissions: contents: read) y eleva privilegios job a job solo cuando haga falta. Fija cada acción de terceros por su SHA de commit completo, no por tag (uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683), porque un tag es mutable y puede reescribirse, como en el caso de tj-actions. Restringe qué acciones se permiten a nivel de organización, protege los entornos de producción con revisores obligatorios y añade StepSecurity Harden-Runner para filtrar el tráfico de salida y OpenSSF Scorecard para medir la postura del repositorio.

GitLab CI

Marca las variables sensibles como protegidas y enmascaradas, de modo que solo se expongan en ramas y tags protegidos. Acota el CI_JOB_TOKEN con la allowlist de proyectos para que un token filtrado no pueda leer otros repositorios. Escribe las rules con cuidado para que los pipelines disparados por forks nunca hereden jobs de confianza, y separa la flota de runners por nivel de riesgo.

Jenkins

Jenkins es históricamente el eslabón más frágil por su superficie de plugins. Parchea de forma agresiva: CVE-2024-23897, un fallo de lectura arbitraria de ficheros en la CLI, permitía leer claves y derivar en ejecución remota. No ejecutes builds en el controlador, usa agentes efímeros, aísla las credenciales por carpeta con el plugin Credentials Binding y mantén activo el sandbox de Script Security para el código Groovy. La gestión de secretos y el hardening SaaS que aplicas al resto del stack valen también aquí.

Dónde encaja la seguridad del pipeline

El hardening del pipeline es una capa de una postura DevSecOps más amplia, no la totalidad. Los controles anteriores cierran las rutas de PPE, fuga de secretos y abuso de runners más comunes, pero una validación externa sigue mereciendo la pena: un tester encadena un workflow con pull_request_target, una condición OIDC laxa y un runner compartido en una ruta de ataque que ningún escáner marca por separado. Eso es justo lo que hace nuestro servicio de SSDLC y DevSecOps: integra seguridad en cada fase del pipeline y valida qué hallazgos son realmente explotables.

Preguntas frecuentes

¿Qué es la ejecución envenenada del pipeline (PPE)?

Es la técnica de inyectar comandos en la definición del pipeline, o en los ficheros que este invoca, para que el sistema CI los ejecute con sus propios privilegios y acceda a secretos y entornos. Se divide en directa (modificar el workflow), indirecta (inyectar en scripts que el workflow llama) y pública (abusar de pipelines disparados por forks). Es el riesgo CICD-SEC-4 del Top 10 de OWASP.

¿Por qué OIDC es mejor que guardar claves de nube como secretos?

Porque elimina la credencial persistente. Con OIDC el pipeline obtiene credenciales temporales de corta vida en el despliegue, ligadas a la identidad del propio workflow, en lugar de una clave estática que vive para siempre en el almacén de secretos y se filtra en cuanto aparece en un log. Un token que caduca en minutos y solo funciona desde tu repositorio tiene un valor casi nulo para un atacante.

¿Puedo usar runners self-hosted con repositorios públicos?

No es recomendable. Un fork puede enviar una pull request que ejecute código arbitrario en tu runner self-hosted, con acceso a la red donde vive. Si los necesitas, resérvalos para repositorios privados, hazlos efímeros y segrégalos por nivel de confianza. Para repositorios públicos, los runners gestionados y aislados del proveedor son la opción segura.

¿En qué se diferencia de la seguridad de la cadena de suministro?

La seguridad de la cadena de suministro se centra en las dependencias que consumes (paquetes envenenados, dependency confusion, typosquatting). La del pipeline CI/CD se centra en la maquinaria que construye y despliega: permisos sobre workflows, higiene de secretos, aislamiento de runners e integridad de artefactos. Son complementarias, y un programa DevSecOps serio cubre ambas.

¿Qué aporta SLSA a un pipeline ya endurecido?

Procedencia verificable: la garantía criptográfica de que el artefacto desplegado salió de un build concreto, a partir de un commit concreto, sin manipulación por el camino. Aunque el pipeline esté endurecido contra PPE y fuga de secretos, sin procedencia firmada no puedes demostrar que el binario en producción es el que tu pipeline generó. SLSA y la firma con Sigstore cierran ese hueco.

Seguridad de pipelines con Secra

En Secra endurecemos y probamos pipelines CI/CD de principio a fin: revisión de workflows y permisos, cacería de vectores PPE, migración a OIDC, aislamiento de runners y verificación de procedencia con SLSA sobre GitHub Actions, GitLab CI y Jenkins. Si quieres una evaluación externa de tu cadena de build y despliegue, escríbenos a través de contacto y te devolveremos una primera valoración.

Sobre el autor

Equipo de Secra Solutions

Ethical hackers certificados OSCP, OSEP, OSWE, CRTO, CRTL y CARTE, con más de 7 años de experiencia en ciberseguridad ofensiva. Autores de los CVE-2025-40652 y CVE-2023-3512.

Compartir artículo