{"id":647,"date":"2026-02-09T10:35:06","date_gmt":"2026-02-09T09:35:06","guid":{"rendered":"https:\/\/secure-pipelines.com\/?p=647"},"modified":"2026-03-24T18:08:04","modified_gmt":"2026-03-24T17:08:04","slug":"defensive-patterns-mitigations-ci-cd-pipeline-attacks","status":"publish","type":"post","link":"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/defensive-patterns-mitigations-ci-cd-pipeline-attacks\/","title":{"rendered":"Patrones Defensivos y Mitigaciones para Ataques a Pipelines CI\/CD"},"content":{"rendered":"<h2>Introducci\u00f3n<\/h2>\n<p>Comprender c\u00f3mo se atacan los pipelines CI\/CD es solo la mitad del panorama. El modelado de amenazas y la taxonom\u00eda de ataques nos proporcionan un mapa del campo de batalla, pero sin patrones defensivos concretos y mitigaciones de ingenier\u00eda, ese conocimiento permanece te\u00f3rico. Esta gu\u00eda cierra la brecha entre la concienciaci\u00f3n y la acci\u00f3n.<\/p>\n<p>El objetivo no es construir una fortaleza impenetrable \u2014 eso no existe. En cambio, nos enfocamos en reducir la superficie de ataque, limitar el radio de explosi\u00f3n cuando algo sale mal y hacer que los pipelines sean lo suficientemente resilientes como para recuperarse r\u00e1pidamente. Cada control descrito aqu\u00ed se relaciona con patrones de ataque del mundo real: pipelines envenenados, robo de credenciales, secuestro de dependencias y manipulaci\u00f3n de artefactos.<\/p>\n<p>Recorreremos las defensas capa por capa \u2014 desde el c\u00f3digo fuente hasta el runtime \u2014 y luego cubriremos las capacidades de detecci\u00f3n y respuesta a incidentes que cierran el ciclo. Ya sea que est\u00e9s asegurando GitHub Actions, GitLab CI, Jenkins o cualquier otra plataforma CI\/CD, los principios siguen siendo los mismos.<\/p>\n<h2>Defensa en Profundidad para CI\/CD<\/h2>\n<p>Ning\u00fan control de seguridad individual es suficiente para proteger un pipeline CI\/CD. Los atacantes son creativos y encontrar\u00e1n la brecha en cualquier defensa de una sola capa. La \u00fanica estrategia viable es la defensa en profundidad: controles superpuestos en cada etapa del ciclo de vida de entrega de software.<\/p>\n<h3>Mapeo de Defensas a los OWASP Top 10 CI\/CD Security Risks<\/h3>\n<p>Los <a href=\"https:\/\/owasp.org\/www-project-top-10-ci-cd-security-risks\/\" target=\"_blank\" rel=\"noopener\">OWASP Top 10 CI\/CD Security Risks<\/a> proporcionan un marco estructurado para comprender qu\u00e9 puede salir mal. Cada riesgo \u2014 desde CICD-SEC-1 (Insufficient Flow Control Mechanisms) hasta CICD-SEC-10 (Insufficient Logging and Visibility) \u2014 requiere mitigaciones espec\u00edficas. Las defensas en esta gu\u00eda est\u00e1n organizadas para abordar estos riesgos de manera sistem\u00e1tica.<\/p>\n<h3>Los Tres Pilares: Prevenci\u00f3n, Detecci\u00f3n, Respuesta<\/h3>\n<ul>\n<li><strong>Prevenci\u00f3n:<\/strong> Controles que detienen los ataques antes de que tengan \u00e9xito \u2014 protecciones de ramas, permisos m\u00ednimos, artefactos firmados, runners ef\u00edmeros.<\/li>\n<li><strong>Detecci\u00f3n:<\/strong> Monitoreo y alertas que revelan anomal\u00edas \u2014 comportamiento inesperado del pipeline, deriva de configuraci\u00f3n, nuevas dependencias, exposici\u00f3n de secretos.<\/li>\n<li><strong>Respuesta:<\/strong> Playbooks y procedimientos para cuando las defensas fallan \u2014 revocaci\u00f3n de credenciales, an\u00e1lisis del radio de explosi\u00f3n, verificaci\u00f3n de integridad de artefactos, investigaci\u00f3n forense.<\/li>\n<\/ul>\n<h3>Defensa en Cada Capa<\/h3>\n<p>Piensa en el pipeline CI\/CD como una cadena de l\u00edmites de confianza:<\/p>\n<ul>\n<li><strong>Fuente:<\/strong> Donde el c\u00f3digo y la configuraci\u00f3n ingresan al pipeline<\/li>\n<li><strong>Build:<\/strong> Donde el c\u00f3digo se compila, se prueba y se empaqueta<\/li>\n<li><strong>Artefacto:<\/strong> Donde los resultados del build se almacenan y distribuyen<\/li>\n<li><strong>Deploy:<\/strong> Donde los artefactos llegan a la infraestructura de producci\u00f3n<\/li>\n<li><strong>Runtime:<\/strong> Donde el software desplegado se ejecuta y se monitorea<\/li>\n<\/ul>\n<p>Cada capa tiene amenazas distintas y requiere defensas distintas. El compromiso en una capa no deber\u00eda propagarse autom\u00e1ticamente a la siguiente.<\/p>\n<h2>Defensas de la Capa de Fuente \u2014 Protecci\u00f3n de las Entradas del Pipeline<\/h2>\n<p>La capa de fuente es donde comienzan la mayor\u00eda de los ataques a CI\/CD. Un atacante que puede modificar c\u00f3digo, definiciones de pipelines o archivos de configuraci\u00f3n controla lo que ejecuta el pipeline. Las defensas de la capa de fuente aseguran que solo los cambios autorizados, revisados y verificados ingresen al pipeline.<\/p>\n<h3>Reglas de Protecci\u00f3n de Ramas<\/h3>\n<p>La protecci\u00f3n de ramas es la primera l\u00ednea de defensa. Como m\u00ednimo, las ramas main y de release deben aplicar:<\/p>\n<ul>\n<li><strong>Pull request reviews obligatorios:<\/strong> Sin push directo a ramas protegidas. Todos los cambios pasan por revisi\u00f3n de c\u00f3digo.<\/li>\n<li><strong>Status checks obligatorios:<\/strong> El CI debe pasar antes del merge. Esto previene la fusi\u00f3n de c\u00f3digo roto o malicioso que evade las pruebas.<\/li>\n<li><strong>Sin force pushes:<\/strong> El force push reescribe el historial y puede usarse para eliminar evidencia de commits maliciosos.<\/li>\n<li><strong>Historial lineal obligatorio:<\/strong> Previene los merge commits que pueden ocultar cambios maliciosos en diffs complejos.<\/li>\n<\/ul>\n<h3>CODEOWNERS para Rutas Sensibles<\/h3>\n<p>No todos los archivos en un repositorio conllevan el mismo riesgo. Las definiciones de pipelines, las plantillas de infrastructure-as-code y las configuraciones de contenedores son objetivos de alto valor. Usa CODEOWNERS para requerir revisi\u00f3n de equipos espec\u00edficos para rutas sensibles:<\/p>\n<pre><code># .github\/CODEOWNERS\n\n# Pipeline definitions require security team review\n.github\/workflows\/    @org\/security-team\n.gitlab-ci.yml        @org\/security-team\nJenkinsfile           @org\/security-team\n\n# Infrastructure as code\nterraform\/            @org\/platform-team @org\/security-team\npulumi\/               @org\/platform-team @org\/security-team\n\n# Container definitions\nDockerfile*           @org\/security-team\ndocker-compose*.yml   @org\/security-team\n\n# Dependency manifests\npackage.json          @org\/security-team\nrequirements.txt      @org\/security-team\ngo.sum                @org\/security-team<\/code><\/pre>\n<h3>Commits Firmados y Verificaci\u00f3n<\/h3>\n<p>La firma de commits proporciona prueba criptogr\u00e1fica de autor\u00eda. Sin ella, un atacante que comprometa el token de acceso de un desarrollador puede enviar commits que parecen provenir de cualquier persona. Habilita la verificaci\u00f3n de firma de commits en ramas protegidas para asegurar que cada commit est\u00e9 firmado con una clave GPG o SSH verificada.<\/p>\n<pre><code># Configure Git to sign commits with SSH key\ngit config --global gpg.format ssh\ngit config --global user.signingkey ~\/.ssh\/id_ed25519.pub\ngit config --global commit.gpgsign true\n\n# Verify a commit signature\ngit verify-commit HEAD<\/code><\/pre>\n<h3>Pol\u00edticas de Revisi\u00f3n de PR<\/h3>\n<p>La revisi\u00f3n de c\u00f3digo es un control humano, y necesita barandillas:<\/p>\n<ul>\n<li><strong>Sin auto-aprobaci\u00f3n:<\/strong> El autor de un PR nunca deber\u00eda poder aprobar sus propios cambios.<\/li>\n<li><strong>Revisores obligatorios del equipo de seguridad<\/strong> para cambios en archivos de pipeline, configuraci\u00f3n de secretos o manifiestos de despliegue.<\/li>\n<li><strong>Descartar revisiones obsoletas:<\/strong> Si se env\u00edan nuevos commits despu\u00e9s de la aprobaci\u00f3n, las aprobaciones anteriores deben descartarse para forzar una nueva revisi\u00f3n.<\/li>\n<li><strong>Requerir revisi\u00f3n de code owners:<\/strong> Combina esto con CODEOWNERS para aplicar requisitos de revisi\u00f3n espec\u00edficos del dominio.<\/li>\n<\/ul>\n<h3>Limitaci\u00f3n de Triggers del Pipeline<\/h3>\n<p>No todos los eventos deben activar una ejecuci\u00f3n completa del pipeline, especialmente una que tenga acceso a secretos:<\/p>\n<ul>\n<li><strong>Restricciones de forks:<\/strong> Los PRs desde forks deben ejecutarse en un contexto restringido sin acceso a los secretos del repositorio.<\/li>\n<li><strong>Permisos de contribuidores:<\/strong> Solo los colaboradores con acceso de escritura deber\u00edan poder activar workflows que acceden a recursos sensibles.<\/li>\n<li><strong>Aprobaci\u00f3n para contribuidores primerizos:<\/strong> Requerir aprobaci\u00f3n manual antes de ejecutar pipelines para nuevos contribuidores.<\/li>\n<\/ul>\n<h2>Defensas de la Capa de Build \u2014 Asegurando el Proceso de Build<\/h2>\n<p>La capa de build es donde el c\u00f3digo se convierte en ejecutable. Un compromiso aqu\u00ed significa que un atacante puede inyectar l\u00f3gica maliciosa en tus artefactos sin modificar el c\u00f3digo fuente. Las defensas de la capa de build se centran en el aislamiento, la efimeralidad y el privilegio m\u00ednimo.<\/p>\n<h3>Runners Ef\u00edmeros<\/h3>\n<p>Los runners CI persistentes acumulan estado: credenciales en cach\u00e9, archivos sobrantes de builds anteriores, variables de entorno que se filtran entre jobs. Los runners ef\u00edmeros eliminan esta clase de riesgo por completo al aprovisionar una VM o contenedor nuevo para cada job y destruirlo inmediatamente despu\u00e9s.<\/p>\n<pre><code># GitHub Actions: Self-hosted ephemeral runner with actions-runner-controller\napiVersion: actions.summerwind.dev\/v1alpha1\nkind: RunnerDeployment\nmetadata:\n  name: ephemeral-runners\nspec:\n  replicas: 5\n  template:\n    spec:\n      ephemeral: true\n      repository: your-org\/your-repo\n      labels:\n        - self-hosted\n        - ephemeral\n        - linux\n      dockerdWithinRunnerContainer: false\n      image: ghcr.io\/actions\/actions-runner:latest\n      resources:\n        limits:\n          cpu: \"2\"\n          memory: \"4Gi\"<\/code><\/pre>\n<h3>Entornos de Build Aislados<\/h3>\n<p>Incluso con runners ef\u00edmeros, los builds que comparten cach\u00e9s o namespaces de red pueden filtrar informaci\u00f3n entre jobs. Asegura:<\/p>\n<ul>\n<li><strong>Sin cach\u00e9s compartidos entre builds no confiables:<\/strong> El envenenamiento de cach\u00e9 es un vector de ataque real. A\u00edsla los cach\u00e9s por rama o por PR.<\/li>\n<li><strong>Pools de runners separados:<\/strong> Los runners de despliegue a producci\u00f3n nunca deben compartirse con los runners de validaci\u00f3n de PRs.<\/li>\n<li><strong>Aislamiento de contenedores:<\/strong> Usa contenedores rootless o microVMs (Firecracker, gVisor) para un aislamiento m\u00e1s fuerte que Docker est\u00e1ndar.<\/li>\n<\/ul>\n<h3>Restricciones de Red Durante los Builds<\/h3>\n<p>Un paso de build comprometido con acceso de red sin restricciones puede exfiltrar secretos a infraestructura controlada por el atacante. Restringe el acceso de red saliente:<\/p>\n<ul>\n<li><strong>Sin internet saliente:<\/strong> La opci\u00f3n m\u00e1s estricta. Todas las dependencias deben provenir de mirrors internos o im\u00e1genes pre-cacheadas.<\/li>\n<li><strong>Solo dominios en lista blanca:<\/strong> Si el acceso a internet es necesario, restr\u00edngelo a registros y repositorios de paquetes conocidos.<\/li>\n<li><strong>Filtrado basado en DNS:<\/strong> Usa pol\u00edticas DNS para bloquear el acceso a dominios no autorizados durante los builds.<\/li>\n<\/ul>\n<pre><code># Kubernetes NetworkPolicy for CI runner pods\napiVersion: networking.k8s.io\/v1\nkind: NetworkPolicy\nmetadata:\n  name: ci-runner-egress-restricted\n  namespace: ci-runners\nspec:\n  podSelector:\n    matchLabels:\n      role: ci-runner\n  policyTypes:\n    - Egress\n  egress:\n    - to:\n        - ipBlock:\n            cidr: 10.0.0.0\/8    # Internal network only\n      ports:\n        - protocol: TCP\n          port: 443              # HTTPS to internal registries\n        - protocol: TCP\n          port: 53               # DNS\n        - protocol: UDP\n          port: 53               # DNS<\/code><\/pre>\n<h3>Im\u00e1genes de Build M\u00ednimas<\/h3>\n<p>Cada herramienta instalada en una imagen de build es una superficie de ataque potencial. Reduce las im\u00e1genes de build al m\u00ednimo necesario:<\/p>\n<ul>\n<li>Usa im\u00e1genes distroless o basadas en Alpine como bases de build.<\/li>\n<li>Elimina shells, gestores de paquetes y utilidades de red de las im\u00e1genes de build de producci\u00f3n cuando sea posible.<\/li>\n<li>Fija los digests de imagen, no los tags, para prevenir ataques a la cadena de suministro basados en tags.<\/li>\n<\/ul>\n<pre><code># Pin by digest, not by tag\nFROM golang:1.22@sha256:a3b21c5d8e... AS builder\nWORKDIR \/app\nCOPY . .\nRUN CGO_ENABLED=0 go build -o \/app\/binary\n\n# Use distroless for the final image\nFROM gcr.io\/distroless\/static-debian12@sha256:f4e8b1c2d9...\nCOPY --from=builder \/app\/binary \/binary\nENTRYPOINT [\"\/binary\"]<\/code><\/pre>\n<h3>Desactivar Modos de Debug en Pipelines de Producci\u00f3n<\/h3>\n<p>El logging de debug y la salida detallada son invaluables durante el desarrollo pero peligrosos en pipelines de producci\u00f3n. Pueden filtrar secretos, rutas internas y detalles de infraestructura. Aseg\u00farate de que <code>ACTIONS_STEP_DEBUG<\/code>, <code>CI_DEBUG_TRACE<\/code> y flags equivalentes est\u00e9n desactivados en las configuraciones de pipelines de producci\u00f3n.<\/p>\n<h2>Defensas de Credenciales e Identidad \u2014 Limitando lo que los Pipelines Pueden Acceder<\/h2>\n<p>Las credenciales son el objetivo m\u00e1s valioso en cualquier pipeline CI\/CD. Un atacante que obtenga una clave de acceso a la nube, un token de despliegue o un secreto de API puede pivotar mucho m\u00e1s all\u00e1 del pipeline en s\u00ed. Las defensas de credenciales se centran en minimizar lo que existe, lo que se puede acceder y por cu\u00e1nto tiempo.<\/p>\n<h3>Permisos M\u00ednimos de Tokens<\/h3>\n<p>El <code>GITHUB_TOKEN<\/code> predeterminado en GitHub Actions tiene permisos amplios. Siempre restr\u00edngelo al m\u00ednimo requerido:<\/p>\n<pre><code># GitHub Actions: Restrict default token permissions\npermissions:\n  contents: read\n  packages: read\n  id-token: write   # Only if using OIDC\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n    steps:\n      - uses: actions\/checkout@v4\n      - run: make build\n\n  deploy:\n    runs-on: ubuntu-latest\n    needs: build\n    permissions:\n      contents: read\n      id-token: write   # For OIDC authentication\n    steps:\n      - name: Authenticate to cloud\n        uses: aws-actions\/configure-aws-credentials@v4\n        with:\n          role-to-assume: arn:aws:iam::123456789012:role\/deploy-role\n          aws-region: us-east-1<\/code><\/pre>\n<h3>OIDC y Workload Identity<\/h3>\n<p>Los secretos de larga duraci\u00f3n almacenados en sistemas CI\/CD son bombas de tiempo. Reempl\u00e1zalos con federaci\u00f3n de identidad de carga de trabajo basada en OIDC siempre que sea posible:<\/p>\n<ul>\n<li><strong>GitHub Actions a AWS:<\/strong> Usa <code>aws-actions\/configure-aws-credentials<\/code> con asunci\u00f3n de rol OIDC.<\/li>\n<li><strong>GitHub Actions a GCP:<\/strong> Usa <code>google-github-actions\/auth<\/code> con Workload Identity Federation.<\/li>\n<li><strong>GitHub Actions a Azure:<\/strong> Usa <code>azure\/login<\/code> con credenciales federadas.<\/li>\n<li><strong>GitLab CI a AWS\/GCP\/Azure:<\/strong> Usa el token OIDC nativo de GitLab (<code>CI_JOB_JWT_V2<\/code>) con federaci\u00f3n del proveedor de nube.<\/li>\n<\/ul>\n<p>Con OIDC, la plataforma CI\/CD emite un JWT de corta duraci\u00f3n, y el proveedor de nube lo intercambia por credenciales temporales. No se almacenan secretos est\u00e1ticos en ning\u00fan lugar.<\/p>\n<h3>Credenciales por Entorno y por Etapa<\/h3>\n<p>Un \u00fanico conjunto de credenciales compartido entre todos los entornos es un radio de explosi\u00f3n catastr\u00f3fico. Segmenta las credenciales:<\/p>\n<ul>\n<li>Desarrollo, staging y producci\u00f3n deben usar cuentas de servicio separadas con permisos separados.<\/li>\n<li>Las etapas de build no deben tener acceso a las credenciales de despliegue.<\/li>\n<li>Las etapas de prueba deben usar infraestructura de prueba aislada, no entornos compartidos.<\/li>\n<\/ul>\n<h3>Sin Secretos en Workflows de PR\/Fork<\/h3>\n<p>Los pull requests desde forks nunca deben tener acceso a los secretos del repositorio. Esta es una mala configuraci\u00f3n com\u00fan que permite a los atacantes exfiltrar secretos enviando un PR malicioso. En GitHub Actions, usa <code>pull_request<\/code> (no <code>pull_request_target<\/code>) para c\u00f3digo no confiable, y nunca pases secretos a pasos que ejecuten c\u00f3digo del PR.<\/p>\n<h3>Integraci\u00f3n con Vault y Secretos Din\u00e1micos<\/h3>\n<p>Para credenciales que no pueden usar OIDC (contrase\u00f1as de bases de datos, claves API para servicios de terceros), usa un gestor de secretos como HashiCorp Vault con secretos din\u00e1micos y de corta duraci\u00f3n:<\/p>\n<pre><code># HashiCorp Vault: Generate short-lived database credentials\nvault read database\/creds\/ci-readonly\n# Returns:\n# Key                Value\n# ---                -----\n# lease_id           database\/creds\/ci-readonly\/abc123\n# lease_duration     1h\n# username           v-ci-readonly-xyz789\n# password           A1B2-C3D4-E5F6-G7H8<\/code><\/pre>\n<p>Los secretos din\u00e1micos se generan bajo demanda, est\u00e1n delimitados a la identidad solicitante y se revocan autom\u00e1ticamente cuando expiran. Incluso si se filtran, la ventana de exposici\u00f3n se mide en minutos, no en meses.<\/p>\n<h3>Auditor\u00eda de Todos los Accesos a Secretos<\/h3>\n<p>Cada recuperaci\u00f3n de secreto debe generar una entrada en el registro de auditor\u00eda. Si tu gestor de secretos no registra los accesos, no tienes forma de investigar un compromiso. Aseg\u00farate de que los registros capturen: qui\u00e9n accedi\u00f3 a qu\u00e9, cu\u00e1ndo, desde qu\u00e9 ejecuci\u00f3n de pipeline y desde qu\u00e9 direcci\u00f3n IP.<\/p>\n<h2>Defensas de la Capa de Artefactos \u2014 Asegurando la Integridad de las Salidas<\/h2>\n<p>Los artefactos de build \u2014 im\u00e1genes de contenedores, binarios, paquetes \u2014 son el puente entre tu pipeline y producci\u00f3n. Si un atacante puede manipular los artefactos despu\u00e9s de construidos, todas las defensas anteriores se vuelven irrelevantes. Las defensas de la capa de artefactos aseguran integridad, procedencia e inmutabilidad.<\/p>\n<h3>Firma de Todos los Artefactos con Sigstore\/Cosign<\/h3>\n<p>La firma de artefactos proporciona prueba criptogr\u00e1fica de que un artefacto fue producido por tu pipeline y no ha sido modificado desde entonces. Cosign de Sigstore hace pr\u00e1ctica la firma sin claves:<\/p>\n<pre><code># Sign a container image using Cosign (keyless, OIDC-based)\ncosign sign --yes ghcr.io\/your-org\/your-app:v1.2.3@sha256:abc123...\n\n# Verify the signature\ncosign verify \\\n  --certificate-identity=https:\/\/github.com\/your-org\/your-app\/.github\/workflows\/build.yml@refs\/heads\/main \\\n  --certificate-oidc-issuer=https:\/\/token.actions.githubusercontent.com \\\n  ghcr.io\/your-org\/your-app:v1.2.3@sha256:abc123...<\/code><\/pre>\n<p>Con la firma sin claves, la clave de firma es ef\u00edmera y est\u00e1 vinculada a la identidad OIDC del workflow CI\/CD. No hay una clave de firma de larga duraci\u00f3n que robar.<\/p>\n<h3>Generaci\u00f3n y Almacenamiento de Provenance SLSA<\/h3>\n<p>SLSA (Supply-chain Levels for Software Artifacts) provenance registra c\u00f3mo, d\u00f3nde y por qui\u00e9n se construy\u00f3 un artefacto. En SLSA Level 3, la provenance es generada por la propia plataforma de build y no puede ser falsificada por el proceso de build:<\/p>\n<pre><code># GitHub Actions: Generate SLSA provenance for container images\n- uses: slsa-framework\/slsa-github-generator\/.github\/workflows\/generator_container_slsa3.yml@v2.0.0\n  with:\n    image: ghcr.io\/your-org\/your-app\n    digest: ${{ steps.build.outputs.digest }}\n  secrets:\n    registry-username: ${{ github.actor }}\n    registry-password: ${{ secrets.GITHUB_TOKEN }}<\/code><\/pre>\n<h3>Almacenamiento Inmutable de Artefactos<\/h3>\n<p>Los artefactos publicados nunca deben sobrescribirse. Si un atacante puede reemplazar una versi\u00f3n publicada, puede inyectar c\u00f3digo malicioso en cada despliegue que haga referencia a esa versi\u00f3n. Configura tus registros para inmutabilidad:<\/p>\n<ul>\n<li><strong>Registros de contenedores:<\/strong> Habilita la inmutabilidad de tags (ECR, GCR, ACR todos lo soportan).<\/li>\n<li><strong>Registros de paquetes:<\/strong> Previene la re-publicaci\u00f3n de versiones existentes.<\/li>\n<li><strong>Almacenamiento de binarios:<\/strong> Usa pol\u00edticas de escritura \u00fanica (S3 Object Lock, pol\u00edticas de retenci\u00f3n de GCS).<\/li>\n<\/ul>\n<h3>Generaci\u00f3n de SBOM y Attestation<\/h3>\n<p>Un Software Bill of Materials (SBOM) lista cada componente en tu artefacto. Generar un SBOM en tiempo de build y atestiguarlo junto al artefacto crea un inventario verificable para la gesti\u00f3n de vulnerabilidades:<\/p>\n<pre><code># Generate SBOM with Syft and attest with Cosign\nsyft ghcr.io\/your-org\/your-app:v1.2.3 -o spdx-json > sbom.spdx.json\ncosign attest --predicate sbom.spdx.json --type spdxjson \\\n  ghcr.io\/your-org\/your-app:v1.2.3@sha256:abc123...<\/code><\/pre>\n<h3>Admission Controllers para Verificaci\u00f3n de Firmas<\/h3>\n<p>Firmar artefactos solo es \u00fatil si verificas las firmas antes del despliegue. Usa admission controllers de Kubernetes para aplicar esto autom\u00e1ticamente:<\/p>\n<pre><code># Kyverno: Require signed images\napiVersion: kyverno.io\/v1\nkind: ClusterPolicy\nmetadata:\n  name: require-signed-images\nspec:\n  validationFailureAction: Enforce\n  background: false\n  rules:\n    - name: verify-cosign-signature\n      match:\n        any:\n          - resources:\n              kinds:\n                - Pod\n      verifyImages:\n        - imageReferences:\n            - \"ghcr.io\/your-org\/*\"\n          attestors:\n            - entries:\n                - keyless:\n                    issuer: \"https:\/\/token.actions.githubusercontent.com\"\n                    subject: \"https:\/\/github.com\/your-org\/*\"<\/code><\/pre>\n<p>Con esta pol\u00edtica en vigor, cualquier imagen de contenedor que carezca de una firma Cosign v\u00e1lida de tus workflows de GitHub Actions ser\u00e1 rechazada en el momento de admisi\u00f3n \u2014 antes de que se ejecute en tu cl\u00faster.<\/p>\n<h2>Defensas de la Capa de Despliegue \u2014 Controlando lo que Llega a Producci\u00f3n<\/h2>\n<p>La capa de despliegue es la \u00faltima puerta antes de que los cambios lleguen a producci\u00f3n. Las defensas aqu\u00ed aseguran que solo los artefactos verificados y aprobados sean desplegados, y que el proceso de despliegue en s\u00ed sea controlado y auditable.<\/p>\n<h3>Aprobaciones Manuales Obligatorias<\/h3>\n<p>Para despliegues en producci\u00f3n, los pipelines automatizados deben pausarse y requerir aprobaci\u00f3n humana expl\u00edcita. Esto proporciona un punto de verificaci\u00f3n final donde un humano puede confirmar que el cambio es esperado, probado y autorizado.<\/p>\n<pre><code># GitHub Actions: Environment with required reviewers\njobs:\n  deploy-production:\n    runs-on: ubuntu-latest\n    environment:\n      name: production\n      url: https:\/\/your-app.example.com\n    steps:\n      - name: Deploy to production\n        run: .\/deploy.sh production<\/code><\/pre>\n<p>En la configuraci\u00f3n del repositorio de GitHub, configura el entorno \u00abproduction\u00bb para requerir aprobaci\u00f3n de revisores designados antes de que el job proceda.<\/p>\n<h3>GitOps con Despliegue Pull-Based<\/h3>\n<p>Los pipelines CI\/CD tradicionales hacen push a producci\u00f3n: el pipeline tiene credenciales para modificar la infraestructura de producci\u00f3n. Esto es una gran superficie de ataque. GitOps con despliegue pull-based invierte el modelo:<\/p>\n<ul>\n<li><strong>El pipeline<\/strong> actualiza un repositorio Git con el estado deseado (image tags, manifiestos).<\/li>\n<li><strong>El cl\u00faster<\/strong> ejecuta un controlador (Flux, ArgoCD) que observa el repositorio Git y extrae los cambios.<\/li>\n<li><strong>El pipeline nunca tiene acceso directo al cl\u00faster.<\/strong> El cl\u00faster extrae de Git, y Git es la \u00fanica fuente de verdad.<\/li>\n<\/ul>\n<pre><code># Flux: GitRepository and Kustomization for pull-based deployment\napiVersion: source.toolkit.fluxcd.io\/v1\nkind: GitRepository\nmetadata:\n  name: app-manifests\n  namespace: flux-system\nspec:\n  interval: 1m\n  url: https:\/\/github.com\/your-org\/app-manifests\n  ref:\n    branch: main\n  secretRef:\n    name: git-credentials\n---\napiVersion: kustomize.toolkit.fluxcd.io\/v1\nkind: Kustomization\nmetadata:\n  name: app-production\n  namespace: flux-system\nspec:\n  interval: 5m\n  path: .\/environments\/production\n  prune: true\n  sourceRef:\n    kind: GitRepository\n    name: app-manifests\n  healthChecks:\n    - apiVersion: apps\/v1\n      kind: Deployment\n      name: your-app\n      namespace: production<\/code><\/pre>\n<h3>Despliegues Canary y Rollback Automatizado<\/h3>\n<p>Incluso con todas las defensas anteriores, un despliegue puede introducir problemas. Los despliegues canary limitan la exposici\u00f3n al desplegar cambios a un peque\u00f1o porcentaje del tr\u00e1fico primero. Si las m\u00e9tricas se degradan, el rollback automatizado revierte el cambio antes de que afecte a todos los usuarios.<\/p>\n<ul>\n<li>Usa herramientas de entrega progresiva como Flagger, Argo Rollouts o funcionalidades canary nativas del proveedor de nube.<\/li>\n<li>Define criterios de \u00e9xito claros: tasa de errores, latencia, m\u00e9tricas de saturaci\u00f3n.<\/li>\n<li>Automatiza los triggers de rollback \u2014 no dependas de humanos para notar y reaccionar a tiempo.<\/li>\n<\/ul>\n<h3>Congelaci\u00f3n de Despliegues<\/h3>\n<p>Durante incidentes activos, ventanas de mantenimiento o per\u00edodos de alto tr\u00e1fico, los despliegues deben congelarse. Implementa pol\u00edticas de congelaci\u00f3n de despliegues que prevengan despliegues iniciados por pipelines durante ventanas especificadas, y asegura que solo los comandantes de incidentes designados puedan anular la congelaci\u00f3n.<\/p>\n<h2>Detecci\u00f3n y Monitoreo \u2014 Saber Cu\u00e1ndo Algo Est\u00e1 Mal<\/h2>\n<p>La prevenci\u00f3n eventualmente fallar\u00e1. Las capacidades de detecci\u00f3n determinan si detectas un compromiso en minutos o en meses. El monitoreo de CI\/CD es un punto ciego para muchas organizaciones \u2014 su SIEM ingiere logs de aplicaciones e infraestructura pero ignora completamente la telemetr\u00eda del pipeline.<\/p>\n<h3>Detecci\u00f3n de Anomal\u00edas en la Ejecuci\u00f3n del Pipeline<\/h3>\n<p>Establece l\u00edneas base para el comportamiento normal del pipeline y genera alertas sobre desviaciones:<\/p>\n<ul>\n<li><strong>Tiempos de ejecuci\u00f3n inusuales:<\/strong> Un build que normalmente toma 5 minutos y de repente toma 30 minutos podr\u00eda indicar cryptomining o exfiltraci\u00f3n de datos.<\/li>\n<li><strong>Pasos inesperados:<\/strong> Nuevos pasos en el pipeline que aparecen sin cambios correspondientes en PRs.<\/li>\n<li><strong>Ejecuci\u00f3n fuera de horario:<\/strong> Ejecuciones de pipelines activadas fuera del horario laboral normal por cuentas inusuales.<\/li>\n<li><strong>Picos de autenticaci\u00f3n fallida:<\/strong> M\u00faltiples intentos fallidos de acceso a secretos desde una sola ejecuci\u00f3n de pipeline.<\/li>\n<\/ul>\n<h3>Alertas de Diff de Dependencias<\/h3>\n<p>Las nuevas dependencias a\u00f1adidas en PRs deben activar revisi\u00f3n automatizada y alertas. Una herramienta de diff de dependencias puede:<\/p>\n<ul>\n<li>Marcar nuevas dependencias a\u00f1adidas en un PR para revisi\u00f3n manual.<\/li>\n<li>Verificar nuevas dependencias contra bases de datos de paquetes maliciosos conocidos.<\/li>\n<li>Verificar que las versiones de dependencias coincidan con las de lockfiles conocidos como buenos.<\/li>\n<li>Alertar sobre dependencias con fechas de publicaci\u00f3n muy recientes (potencial typosquatting).<\/li>\n<\/ul>\n<h3>Escaneo de Secretos<\/h3>\n<p>Los secretos se filtran a trav\u00e9s de commits, logs y artefactos. Combina m\u00faltiples enfoques de escaneo:<\/p>\n<ul>\n<li><strong>Pre-commit hooks:<\/strong> Herramientas como <code>gitleaks<\/code> o <code>trufflehog<\/code> capturan secretos antes de que entren al repositorio.<\/li>\n<li><strong>Escaneo en el pipeline:<\/strong> Escanea las salidas del build y los logs en busca de credenciales expuestas accidentalmente.<\/li>\n<li><strong>GitHub secret scanning \/ GitLab secret detection:<\/strong> Escaneo nativo de la plataforma que cubre eventos de push y commits hist\u00f3ricos.<\/li>\n<li><strong>Alertas del programa de partners:<\/strong> El programa de partners de secret scanning de GitHub notifica a los proveedores de servicios cuando sus tokens est\u00e1n expuestos, permitiendo la revocaci\u00f3n autom\u00e1tica.<\/li>\n<\/ul>\n<pre><code># Pre-commit hook with gitleaks\n# .pre-commit-config.yaml\nrepos:\n  - repo: https:\/\/github.com\/gitleaks\/gitleaks\n    rev: v8.18.0\n    hooks:\n      - id: gitleaks<\/code><\/pre>\n<h3>Monitoreo de Deriva de Configuraci\u00f3n<\/h3>\n<p>Las definiciones de pipelines deben cambiar a trav\u00e9s del proceso normal de PR. Monitorea cambios inesperados:<\/p>\n<ul>\n<li>Alerta cuando los archivos de workflow, configuraciones de CI o manifiestos de despliegue cambian fuera de PRs aprobados.<\/li>\n<li>Rastrea los cambios de permisos del pipeline a lo largo del tiempo.<\/li>\n<li>Detecta nuevos secretos de pipeline que se a\u00f1aden sin solicitudes de cambio correspondientes.<\/li>\n<\/ul>\n<h3>Integraci\u00f3n SIEM para Logs de Auditor\u00eda de CI\/CD<\/h3>\n<p>Reenv\u00eda los logs de auditor\u00eda de CI\/CD a tu SIEM junto con los logs de aplicaciones e infraestructura. Las fuentes de logs clave incluyen:<\/p>\n<ul>\n<li>GitHub Audit Log (nivel de organizaci\u00f3n y empresa)<\/li>\n<li>GitLab Audit Events<\/li>\n<li>Logs de sistema y de build de Jenkins<\/li>\n<li>Logs de auditor\u00eda del proveedor de nube para llamadas API iniciadas por el pipeline (CloudTrail, Cloud Audit Logs, Azure Activity Log)<\/li>\n<\/ul>\n<p>Correlaciona la actividad del pipeline con cambios en la infraestructura de nube. Si una ejecuci\u00f3n de pipeline coincide con modificaciones inesperadas de pol\u00edticas IAM o creaci\u00f3n de recursos, esa es una alerta de alta prioridad.<\/p>\n<h2>Respuesta a Incidentes para CI\/CD \u2014 Cuando las Defensas Fallan<\/h2>\n<p>Cuando se detecta \u2014 o se sospecha \u2014 un compromiso de CI\/CD, la velocidad importa. El atacante puede a\u00fan tener acceso activo, y cada minuto de retraso ampl\u00eda el radio de explosi\u00f3n. Un playbook de respuesta a incidentes preparado para escenarios espec\u00edficos de CI\/CD es esencial.<\/p>\n<h3>Acciones Inmediatas: Contener el Compromiso<\/h3>\n<ul>\n<li><strong>Revocar credenciales comprometidas inmediatamente.<\/strong> Rota todos los secretos a los que el pipeline comprometido ten\u00eda acceso. Esto incluye credenciales de proveedores de nube, tokens de API, contrase\u00f1as de bases de datos y los propios tokens de la plataforma CI\/CD.<\/li>\n<li><strong>Desactivar el pipeline comprometido.<\/strong> Previene ejecuciones adicionales hasta que la investigaci\u00f3n est\u00e9 completa.<\/li>\n<li><strong>Poner en cuarentena los runners afectados.<\/strong> Si usas runners persistentes, a\u00edslalos de la red para an\u00e1lisis forense.<\/li>\n<\/ul>\n<h3>An\u00e1lisis del Radio de Explosi\u00f3n<\/h3>\n<p>Determina a qu\u00e9 pudo haber accedido el atacante:<\/p>\n<ul>\n<li>\u00bfQu\u00e9 secretos estaban disponibles para el job comprometido?<\/li>\n<li>\u00bfA qu\u00e9 recursos de nube pod\u00edan acceder esas credenciales?<\/li>\n<li>\u00bfQu\u00e9 artefactos se produjeron durante el per\u00edodo comprometido?<\/li>\n<li>\u00bfA qu\u00e9 entornos se despleg\u00f3 desde el pipeline comprometido?<\/li>\n<\/ul>\n<h3>Verificaci\u00f3n de Integridad de Artefactos<\/h3>\n<p>Verifica si los artefactos publicados fueron manipulados:<\/p>\n<ul>\n<li>Verifica las firmas en todos los artefactos publicados durante la ventana comprometida.<\/li>\n<li>Compara los checksums de artefactos contra builds conocidos como buenos.<\/li>\n<li>Si la integridad del artefacto no puede verificarse, reconstruye y republica desde commits de fuente conocidos como buenos.<\/li>\n<li>Notifica a los consumidores downstream si se distribuyeron artefactos potencialmente comprometidos.<\/li>\n<\/ul>\n<h3>Investigaci\u00f3n Forense<\/h3>\n<p>Recopila evidencia de m\u00faltiples fuentes:<\/p>\n<ul>\n<li><strong>Logs de runners:<\/strong> \u00bfQu\u00e9 comandos se ejecutaron? \u00bfQu\u00e9 conexiones de red se realizaron?<\/li>\n<li><strong>Logs de auditor\u00eda de API:<\/strong> \u00bfQu\u00e9 llamadas API hizo el atacante usando credenciales del pipeline?<\/li>\n<li><strong>Historial de Git:<\/strong> \u00bfSe modificaron commits o ramas? Verifica force pushes o reescritura del historial.<\/li>\n<li><strong>Logs de auditor\u00eda de la nube:<\/strong> \u00bfQu\u00e9 cambios de infraestructura realizaron las cuentas de servicio del pipeline?<\/li>\n<\/ul>\n<h3>Recuperaci\u00f3n Post-Incidente<\/h3>\n<p>Despu\u00e9s de la contenci\u00f3n e investigaci\u00f3n, restaura las operaciones seguras:<\/p>\n<ul>\n<li><strong>Rota todos los secretos<\/strong> que eran accesibles al pipeline comprometido, incluso si no hay evidencia de que fueron exfiltrados.<\/li>\n<li><strong>Revisa y ajusta los permisos del pipeline.<\/strong> El incidente probablemente revel\u00f3 alcances de permisos m\u00e1s amplios de lo necesario.<\/li>\n<li><strong>Actualiza las reglas de monitoreo<\/strong> bas\u00e1ndote en los indicadores de compromiso descubiertos durante la investigaci\u00f3n.<\/li>\n<li><strong>Realiza un post-mortem sin culpa<\/strong> enfocado en qu\u00e9 cambios sist\u00e9micos previenen la recurrencia.<\/li>\n<\/ul>\n<h3>Plantilla de Playbook de Respuesta a Incidentes CI\/CD<\/h3>\n<pre><code>## CI\/CD Security Incident Playbook\n\n### Phase 1: Detection &amp; Triage (0-15 minutes)\n- [ ] Confirm the alert is a true positive\n- [ ] Classify severity (P1: active compromise, P2: suspected compromise, P3: policy violation)\n- [ ] Notify the incident commander and security team\n\n### Phase 2: Containment (15-60 minutes)\n- [ ] Revoke compromised credentials\n- [ ] Disable affected pipelines\n- [ ] Isolate affected runners\n- [ ] Block attacker's access (revoke tokens, disable accounts)\n\n### Phase 3: Investigation (1-24 hours)\n- [ ] Collect runner logs, audit logs, git history\n- [ ] Determine blast radius (credentials, artifacts, deployments)\n- [ ] Identify attack vector (how did the attacker get in?)\n- [ ] Check artifact integrity for the compromised period\n\n### Phase 4: Recovery (24-72 hours)\n- [ ] Rotate all potentially compromised secrets\n- [ ] Rebuild and republish affected artifacts from known-good source\n- [ ] Redeploy affected environments from verified artifacts\n- [ ] Restore pipeline operations with tightened controls\n\n### Phase 5: Post-Incident (1-2 weeks)\n- [ ] Conduct blameless post-mortem\n- [ ] Document lessons learned and update this playbook\n- [ ] Implement systemic improvements to prevent recurrence\n- [ ] Update detection rules based on IOCs discovered<\/code><\/pre>\n<h2>Conclusi\u00f3n<\/h2>\n<p>La seguridad de CI\/CD no es una lista de verificaci\u00f3n que completas una vez y olvidas. Es una pr\u00e1ctica de ingenier\u00eda continua que evoluciona con tus pipelines, tu infraestructura y el panorama de amenazas. Los atacantes seguir\u00e1n apuntando a la cadena de suministro de software porque ofrece alto apalancamiento \u2014 un solo pipeline comprometido puede afectar cada despliegue, cada entorno y cada cliente.<\/p>\n<p>Las defensas en esta gu\u00eda est\u00e1n organizadas por capa, pero los puntos de inicio de mayor impacto cruzan todas las capas:<\/p>\n<ul>\n<li><strong>Runners ef\u00edmeros<\/strong> eliminan clases enteras de ataques de persistencia y filtraci\u00f3n de estado.<\/li>\n<li><strong>Permisos m\u00ednimos<\/strong> (alcance de tokens, OIDC, credenciales por entorno) limitan lo que un atacante puede hacer incluso despu\u00e9s de obtener acceso al pipeline.<\/li>\n<li><strong>Artefactos firmados con control de admisi\u00f3n<\/strong> aseguran que los artefactos manipulados no puedan llegar a producci\u00f3n.<\/li>\n<li><strong>Detecci\u00f3n y registro de auditor\u00eda<\/strong> cierran la brecha de visibilidad que permite que los compromisos pasen desapercibidos durante meses.<\/li>\n<\/ul>\n<p>Comienza con estos controles de alto impacto. A\u00f1ade defensas adicionales a medida que tu programa de seguridad madure. Y siempre asume que tu pipeline ser\u00e1 un objetivo \u2014 porque lo ser\u00e1.<\/p>\n<p>En el pr\u00f3ximo art\u00edculo de esta serie, recorreremos la implementaci\u00f3n de estas defensas en un pipeline real de GitHub Actions, con un ejemplo funcional completo que puedes adaptar para tus propios repositorios.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introducci\u00f3n Comprender c\u00f3mo se atacan los pipelines CI\/CD es solo la mitad del panorama. El modelado de amenazas y la taxonom\u00eda de ataques nos proporcionan un mapa del campo de batalla, pero sin patrones defensivos concretos y mitigaciones de ingenier\u00eda, ese conocimiento permanece te\u00f3rico. Esta gu\u00eda cierra la brecha entre la concienciaci\u00f3n y la acci\u00f3n. &#8230; <a title=\"Patrones Defensivos y Mitigaciones para Ataques a Pipelines CI\/CD\" class=\"read-more\" href=\"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/defensive-patterns-mitigations-ci-cd-pipeline-attacks\/\" aria-label=\"Leer m\u00e1s sobre Patrones Defensivos y Mitigaciones para Ataques a Pipelines CI\/CD\">Leer m\u00e1s<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[55,60],"tags":[],"post_folder":[],"class_list":["post-647","post","type-post","status-publish","format-standard","hentry","category-ci-cd-security","category-threats-attacks"],"_links":{"self":[{"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts\/647","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/comments?post=647"}],"version-history":[{"count":1,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts\/647\/revisions"}],"predecessor-version":[{"id":659,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts\/647\/revisions\/659"}],"wp:attachment":[{"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/media?parent=647"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/categories?post=647"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/tags?post=647"},{"taxonomy":"post_folder","embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/post_folder?post=647"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}