Les 10 risques de sécurité les plus critiques dans les pipelines CI/CD et comment les atténuer.
Le Top 10 des Risques de Sécurité CI/CD de l'OWASP identifie les risques de sécurité les plus significatifs dans les pipelines d'Intégration Continue et de Livraison Continue. Les pipelines CI/CD sont des cibles de grande valeur car ils ont un accès direct au code source, aux secrets et aux environnements de production. Ce guide couvre le contrôle des flux, la gestion des identités, les attaques de dépendances, l'empoisonnement de pipelines, et bien plus encore.
Les mécanismes de contrôle de flux insuffisants permettent aux attaquants de pousser du code malveillant à travers les pipelines CI/CD sans examen ni porte d'approbation appropriés. Cela inclut le contournement des règles de protection de branches, l'absence d'approbations requises et le manque d'application des contrôles sur qui peut déclencher des déploiements en production.
Sans contrôle de flux approprié, un seul compte développeur compromis ou un initié malveillant peut pousser du code directement en production, contournant la revue de code, les analyses de sécurité et les processus d'approbation. Cela peut conduire au déploiement de portes dérobées, de code d'exfiltration de données ou de charges utiles destructives.
# No branch protection Eanyone can push directly to main name: Deploy on: push: branches: [main] # Triggers on any push to main jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: | # Deploys immediately Eno approval gate! kubectl apply -f deploy.yaml kubectl rollout status deployment/myapp
name: Deploy on: push: branches: [main] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm test deploy: needs: test runs-on: ubuntu-latest environment: production # Requires manual approval steps: - uses: actions/checkout@v4 - run: | kubectl apply -f deploy.yaml kubectl rollout status deployment/myapp
Les systèmes CI/CD impliquent de multiples identités : utilisateurs humains, comptes de service, jetons de bot et identités machine. Une gestion IAM inadéquate permet un accès trop permissif aux dépôts, pipelines et cibles de déploiement. Les identifiants partagés, les comptes obsolètes et l'absence de MFA aggravent le risque.
Des identités CI/CD compromises avec des permissions excessives peuvent modifier les pipelines, accéder aux secrets, altérer les artefacts de build et déployer en production. Les comptes de service partagés rendent impossible l'audit de qui a effectué une action.
# Over-permissive workflow with admin token name: CI on: push permissions: write-all # Full permissions to everything! jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: | # Using org-wide PAT with admin access curl -H "Authorization: token ${{ secrets.ADMIN_PAT }}" \ https://api.github.com/repos/org/other-repo/contents/
name: CI on: push permissions: contents: read # Minimum required permissions packages: write # Only what's needed jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/create-github-app-token@v1 # Scoped app token id: app-token with: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} repositories: other-repo # Scoped to specific repo
Les pipelines CI/CD récupèrent des dépendances depuis des registres de packages externes (npm, PyPI, Maven, Docker Hub). Les attaquants exploitent cela par la confusion de dépendances, le typosquatting, la compromission de comptes de mainteneurs et les packages malveillants. Une seule dépendance empoisonnée peut exécuter du code arbitraire pendant le build.
Les dépendances malveillantes exécutent du code lors de l'installation (scripts postinstall, setup.py), volant des secrets, injectant des portes dérobées dans les artefacts de build ou établissant une persistance. Les attaques de la chaîne d'approvisionnement comme SolarWinds et Codecov démontrent l'impact catastrophique.
pipeline { agent any stages { stage('Build') { steps { // No lockfile verification, no integrity checks sh 'npm install' // Fetches latest Ecould be compromised! sh 'pip install -r requirements.txt' // No hash verification } } } }
pipeline { agent any stages { stage('Build') { steps { // Use lockfile with integrity verification sh 'npm ci' // Uses package-lock.json sh 'npm audit --audit-level=high' // Python: verify hashes from lockfile sh 'pip install --require-hashes -r requirements.lock' } } stage('SCA Scan') { steps { // Software Composition Analysis sh 'trivy fs --scanners vuln,secret .' } } } }
L'Exécution de Pipeline Empoisonnée (PPE) se produit lorsque des attaquants peuvent modifier les définitions de pipeline CI/CD ou injecter du code malveillant qui s'exécute dans le contexte du pipeline. Cela peut se produire par la manipulation de fichiers de configuration de pipeline dans des branches, des pull requests depuis des forks ou la modification de modèles de pipeline partagés.
Un attaquant qui peut modifier la configuration du pipeline obtient l'accès à tous les secrets, identifiants et permissions disponibles pour le pipeline. Il peut exfiltrer des secrets, altérer les résultats de build ou déployer du code malveillant Ele tout dans un contexte d'exécution de confiance.
# Runs pipeline from fork PRs with access to secrets name: CI on: pull_request_target: # Runs in base repo context with secrets! branches: [main] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} # Checks out fork code! - run: make build # Fork's Makefile executes with secrets access env: DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
# Separate workflows: untrusted build + trusted deploy name: CI on: pull_request: # No secret access for PR builds branches: [main] permissions: contents: read jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 # Checks out merge commit (safe) - run: npm ci && npm test # No secrets needed for build/test - uses: actions/upload-artifact@v4 with: name: build-output path: dist/
Les Contrôles d'Accès Basés sur le Pipeline (PBAC) régissent les ressources auxquelles un pipeline peut accéder : comptes cloud, clusters Kubernetes, bases de données et services internes. Un PBAC insuffisant signifie que les pipelines ont un accès plus large que nécessaire, violant le principe du moindre privilège.
Les pipelines sur-privilégiés peuvent être exploités pour accéder à des ressources bien au-delà de leur portée prévue. Un pipeline de build compromis pour un service mineur pourrait être utilisé pour accéder aux bases de données de production, modifier l'infrastructure ou pivoter vers d'autres environnements.
# Pipeline with admin-level cloud credentials deploy: stage: deploy script: - aws configure set aws_access_key_id $AWS_ACCESS_KEY - aws configure set aws_secret_access_key $AWS_SECRET_KEY # This key has AdministratorAccess policy! - aws s3 sync dist/ s3://my-bucket/ - aws ecs update-service --cluster prod --service myapp # Same credentials could access ANY AWS resource
name: Deploy on: push: branches: [main] permissions: id-token: write # For OIDC contents: read jobs: deploy: runs-on: ubuntu-latest steps: - uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: arn:aws:iam::123456:role/deploy-s3-only # Scoped role: only s3:PutObject on specific bucket aws-region: us-east-1 - run: aws s3 sync dist/ s3://my-bucket/
Les pipelines CI/CD gèrent de nombreux identifiants : clés API, jetons cloud, mots de passe de registre, clés SSH et certificats de signature. Une mauvaise hygiène des identifiants inclut le codage en dur de secrets dans les fichiers de pipeline, l'affichage de secrets dans les journaux, l'utilisation de stockage de secrets non chiffré et l'absence de rotation des identifiants.
Les identifiants CI/CD divulgués sont l'un des vecteurs d'accès initial les plus courants. Les secrets exposés dans les journaux de build, commités dans les dépôts ou stockés sans chiffrement fournissent aux attaquants un accès direct aux systèmes de production, comptes cloud et registres d'artefacts.
#!/bin/bash # Secrets hardcoded and leaked in logs export DOCKER_PASSWORD="MyS3cret!" # Hardcoded! echo "Logging in with $DOCKER_PASSWORD" # Printed to logs! docker login -u admin -p "$DOCKER_PASSWORD" registry.example.com docker push registry.example.com/myapp:latest # AWS credentials in environment Evisible in process listing export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCY" aws s3 cp artifact.zip s3://releases/
name: Publish on: push: tags: ['v*'] jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: docker/login-action@v3 # Handles credentials securely with: registry: registry.example.com username: ${{ secrets.DOCKER_USER }} # Masked in logs password: ${{ secrets.DOCKER_TOKEN }} # Short-lived token - run: | docker build -t registry.example.com/myapp:${{ github.ref_name }} . docker push registry.example.com/myapp:${{ github.ref_name }}
Les systèmes CI/CD (Jenkins, GitLab, runners GitHub Actions) fonctionnent souvent avec des configurations par défaut insécurisées. Cela inclut des versions logicielles obsolètes, des interfaces de gestion exposées, des fonctionnalités de sécurité désactivées, un accès réseau trop permissif et des runners auto-hébergés partagés entre projets.
Une infrastructure CI/CD mal configurée peut être exploitée pour obtenir un accès non autorisé aux environnements de build, intercepter des secrets ou pivoter vers des réseaux internes. Les runners auto-hébergés partagés permettent des attaques inter-projets où un workflow compromis affecte les autres.
// Jenkins with insecure configuration // - Script console enabled without auth // - Agent-to-controller access unrestricted // - Outdated plugins with known CVEs pipeline { agent any // Runs on any available agent Eno isolation stages { stage('Build') { steps { // Running as root on shared agent sh 'whoami' // root sh 'docker build -t myapp .' } } } }
pipeline { agent { kubernetes { // Ephemeral, isolated pod per build yaml """ apiVersion: v1 kind: Pod spec: securityContext: runAsNonRoot: true runAsUser: 1000 containers: - name: builder image: builder:1.2.3 securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true """ } } stages { stage('Build') { steps { container('builder') { sh 'make build' } } } } }
Les pipelines CI/CD s'intègrent souvent avec des services tiers : outils de qualité de code, scanners de sécurité, systèmes de notification et plateformes de déploiement. Ces intégrations reçoivent des jetons d'accès et des permissions, créant une chaîne de confiance. L'utilisation non gouvernée signifie aucune visibilité sur ce que les services tiers peuvent accéder ou faire.
Un service tiers compromis (comme la brèche Codecov) peut accéder au code source, aux secrets et aux artefacts de build. Sans gouvernance, les équipes peuvent accorder des permissions excessives à des services inconnus, créant des vecteurs d'attaque invisibles dans la chaîne d'approvisionnement.
name: CI on: push jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm test # Unknown third-party action with full repo access - uses: random-org/code-analysis@main # Unpinned, unvetted! with: token: ${{ secrets.GITHUB_TOKEN }} # Full token access! # Uploading coverage to external service with repo token - run: | bash <(curl -s https://example.com/uploader.sh) # Remote script!
name: CI on: push permissions: contents: read jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm test # Vetted action pinned to SHA - uses: github/codeql-action/analyze@8a470fddafa5cbc14 # Pinned SHA # Upload via official CLI tool, not remote scripts - uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 with: token: ${{ secrets.CODECOV_TOKEN }} # Scoped token fail_ci_if_error: true
Les artefacts de build (images de conteneurs, binaires, packages) transitent par les pipelines CI/CD vers la production. Sans validation d'intégrité, les artefacts peuvent être altérés à n'importe quel point : pendant le build, en transit, dans le registre d'artefacts ou au moment du déploiement. Cela brise la chaîne de confiance du code à la production.
Les artefacts altérés peuvent contenir des portes dérobées, des logiciels malveillants ou une logique modifiée. Sans signature et vérification, il n'y a aucun moyen de détecter si un artefact a été modifié après le build. Les attaquants peuvent remplacer des images légitimes dans les registres ou intercepter des artefacts en transit.
#!/bin/bash # Build and deploy without any integrity checks docker build -t myregistry.com/app:latest . docker push myregistry.com/app:latest # On deployment side Eno verification docker pull myregistry.com/app:latest # Could be tampered! docker run myregistry.com/app:latest # Tag is mutable!
name: Build and Sign on: push: tags: ['v*'] permissions: id-token: write packages: write jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: | # Build with immutable tag (SHA) docker build -t myregistry.com/app:${{ github.sha }} . docker push myregistry.com/app:${{ github.sha }} - uses: sigstore/cosign-installer@v3 - run: | # Sign image with keyless signing (Sigstore) cosign sign myregistry.com/app:${{ github.sha }} # Generate and attach SBOM syft myregistry.com/app:${{ github.sha }} -o spdx-json > sbom.json cosign attest --predicate sbom.json myregistry.com/app:${{ github.sha }}
Les environnements CI/CD génèrent des événements de sécurité critiques : exécutions de pipelines, changements de configuration, accès aux secrets et activités de déploiement. Sans journalisation et surveillance complètes, les activités malveillantes dans le pipeline passent inaperçues et la réponse aux incidents est sévèrement entravée.
Sans visibilité sur les activités CI/CD, les attaquants peuvent modifier les pipelines, exfiltrer des secrets et altérer des artefacts sans déclencher d'alertes. L'absence de pistes d'audit rend impossible la détermination de la portée et de l'impact d'une violation.
#!/bin/bash # Pipeline with no logging or audit trail echo "Starting deployment..." kubectl apply -f deploy.yaml echo "Done." # No record of: who triggered this, what changed, # which image was deployed, what secrets were accessed # Build logs expire after 30 days with no archival
name: Audited Deploy on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Log deployment metadata run: | echo "=== Deployment Audit Log ===" echo "Triggered by: ${{ github.actor }}" echo "Commit: ${{ github.sha }}" echo "Ref: ${{ github.ref }}" echo "Workflow: ${{ github.workflow }}" echo "Run ID: ${{ github.run_id }}" - name: Deploy with audit run: | kubectl apply -f deploy.yaml 2>&1 | tee deploy.log # Send audit event to SIEM curl -X POST "${{ secrets.SIEM_WEBHOOK }}" \ -d '{"event":"deploy","actor":"${{ github.actor }}","sha":"${{ github.sha }}"}'
| ID | Vulnérabilité | Sévérité | Atténuation Clé |
|---|---|---|---|
| CICD-SEC-1 | Mécanismes de Contrôle de Flux Insuffisants | Critical | Protection de branches, approbations requises, portes d'environnement |
| CICD-SEC-2 | Gestion Inadéquate des Identités et des Accès | Critical | Jetons au moindre privilège, OIDC, MFA, rotation des identifiants |
| CICD-SEC-3 | Abus de la Chaîne de Dépendances | Critical | Fichiers de verrouillage, vérification de hachage, registres privés, SCA |
| CICD-SEC-4 | Exécution de Pipeline Empoisonnée (PPE) | Critical | Séparer build/déploiement, définitions de pipeline immuables, contrôles de forks |
| CICD-SEC-5 | PBAC Insuffisant | High | Fédération OIDC, rôles IAM ciblés, séparation des environnements |
| CICD-SEC-6 | Hygiène des Identifiants Insuffisante | Critical | Gestion des secrets, masquage des journaux, rotation, analyse de secrets |
| CICD-SEC-7 | Configuration Système Insécurisée | High | Runners éphémères, non-root, mise à jour des plugins, isolation réseau |
| CICD-SEC-8 | Utilisation Non Gouvernée de Services Tiers | High | Inventaire des services, épinglage SHA, pas de scripts distants |
| CICD-SEC-9 | Validation de l'Intégrité des Artefacts Inadéquate | High | Signature Cosign, contrôle d'admission, provenance SLSA |
| CICD-SEC-10 | Journalisation et Visibilité Insuffisantes | Medium | Journalisation d'audit, intégration SIEM, alertes, rétention des journaux |