Security musings

Catégories

Tags

🔍 Licence d'Utilisation 🔍

Sauf mention contraire, le contenu de ce blog est sous licence CC BY-NC-ND 4.0.

© 2025 à 2042 Sébastien Gioria. Tous droits réservés.

L’infrastructure CI/CD (Continuous Integration / Continuous Delivery) n’est plus simplement un outil de productivité pour les développeurs. C’est devenu la cible numéro un des attaques de Supply Chain.

Pourquoi ? Parce que la CI/CD est l’endroit magique où le code source (lisible par l’humain) devient un artefact exécutable (binaire, image docker). Si un attaquant contrôle cette usine de transformation, il n’a pas besoin de modifier votre code source sur Git : il peut injecter des malwares directement lors de la compilation ou du packaging, rendant l’attaque invisible aux revues de code traditionnelles.

OWASP Top 10 CI/CD Security Risks Source : OWASP Top 10 CI/CD Security Risks

OWASP Top 10 CI/CD : Vue d’ensemble

Le projet OWASP a identifié les 10 risques critiques dans les pipelines CI/CD. Voici un tableau récapitulatif avec des exemples d’attaques connues :

# Risque OWASP Description Attaque Connue / Exemple
CICD-SEC-1 Insufficient Flow Control Mechanisms Absence de contrôles sur qui peut déclencher des builds et avec quels privilèges Codecov (2021) : Script bash modifié dans le pipeline pour exfiltrer des variables d’environnement
CICD-SEC-2 Inadequate Identity and Access Management Gestion faible des permissions et tokens d’accès Travis CI (2021) : Tokens API exposés permettant l’accès à des milliers de projets
CICD-SEC-3 Dependency Chain Abuse Compromission via des dépendances non vérifiées dans le pipeline event-stream NPM (2018) : Package compromis utilisé dans des builds automatisés
CICD-SEC-4 Poisoned Pipeline Execution (PPE) Injection de code malveillant via modification de fichiers de configuration CI PPE via Pull Request : Modification de .github/workflows/ci.yml pour exécuter du code arbitraire
CICD-SEC-5 Insufficient PBAC (Pipeline-Based Access Controls) Permissions trop larges données aux pipelines SolarWinds Orion (2020) : Compromission du système de build pour injecter une backdoor
CICD-SEC-6 Insufficient Credential Hygiene Secrets mal gérés, hardcodés ou exposés dans les logs Uber (2016) : Clés AWS trouvées dans un repo GitHub privé accessible via CI
CICD-SEC-7 Insecure System Configuration Configurations par défaut non sécurisées Jenkins non patchés : Exploitation de CVE pour RCE (Remote Code Execution)
CICD-SEC-8 Ungoverned Usage of 3rd Party Services Intégration de services tiers non audités Dependency Confusion : Packages internes remplacés par des versions publiques malveillantes
CICD-SEC-9 Improper Artifact Integrity Validation Absence de vérification de l’intégrité des artefacts produits Docker Hub : Images compromises sans signature/vérification déployées en production
CICD-SEC-10 Insufficient Logging and Visibility Logs incomplets rendant la détection d’intrusion impossible Après-coup SolarWinds : Difficulté à retracer l’historique exact de la compromission

Cet article se base sur l’OWASP Top 10 CI/CD Security Risks et la Cheat Sheet OWASP pour vous donner les clés de la forteresse.

1. Anatomie des Attaques : Quand le Pipeline se retourne contre vous

Pour comprendre comment sécuriser, il faut comprendre comment on attaque. Oubliez les dépendances NPM un instant, ici on parle de pirater l’infrastructure.

Le Spectre de SolarWinds et Codecov

L’attaque SolarWinds a prouvé qu’on pouvait compromettre des milliers de clients gouvernementaux en modifiant le système de build (le serveur TeamCity) plutôt que le code source.

Plus proche de nous, l’affaire Codecov (2021) est un cas d’école de vol de secrets.

  • Le vecteur : Des attaquants ont modifié le script Bash de l’uploader Codecov (utilisé dans la CI de milliers d’entreprises).
  • L’attaque : Ils ont ajouté une ligne curl silencieuse qui envoyait toutes les variables d’environnement (AWS_SECRET_KEY, GITHUB_TOKEN, etc.) vers leur serveur.
  • Le résultat : Une fuite massive de secrets de production, sans toucher aux applications elles-mêmes.

La Menace #1 : PPE (Poisoned Pipeline Execution)

C’est le risque majeur selon l’OWASP. Un attaquant externe (ou un contributeur malveillant) soumet une Pull Request sur votre dépôt public. Cette PR ne touche pas au code de l’application, mais modifie le fichier de configuration du pipeline (ex: .github/workflows/ci.yml).

Si votre CI est configurée pour exécuter automatiquement les tests des PRs, elle va exécuter le script malveillant de l’attaquant dans votre contexte, avec vos secrets et vos crédits de calcul.

2. Analyse STRIDE appliquée à la CI/CD

Passons votre chaîne de build au crible de la méthode STRIDE pour identifier les failles structurelles.

Menace (STRIDE) Scénario d’attaque concret sur GitHub/GitLab
Spoofing (Usurpation) Committer Impersonation : Un attaquant configure son client git local avec user.email="votre.techlead@company.com" et pousse du code. Si la signature GPG n’est pas forcée, GitHub affichera le commit comme venant du Tech Lead.
Tampering (Modification) Artifact Injection : L’attaquant modifie le binaire compilé pendant le build, juste avant qu’il ne soit empaqueté ou uploadé, insérant une backdoor (comme dans l’attaque SolarWinds).
Repudiation (Répudiation) Log Wiping : Après avoir exfiltré des secrets via un runner, l’attaquant supprime les logs du job ou force l’annulation du build pour masquer ses traces.
Info Disclosure (Divulgation) Secret Echoing : La faute classique : un développeur utilise echo $PASSWORD pour débugger, ou un outil crashe et dump son contexte mémoire (incluant les clés API) dans les logs publics de la console CI.
Denial of Service (Déni) Resource Exhaustion : Une ferme de bots ouvre des centaines de PRs sur votre repo public pour déclencher des builds GitHub Actions et miner de la cryptomonnaie avec vos minutes gratuites, bloquant vos déploiements légitimes.
Elevation of Privilege (Élévation) Runner Breakout : L’attaquant parvient à “s’échapper” du conteneur Docker qui exécute le job pour prendre le contrôle de la machine hôte (le Runner), accédant ainsi au réseau interne de l’entreprise (si self-hosted).

3. Arsenal de Défense : Outils et Configurations

La sécurisation n’est pas une option, c’est une configuration. Voici les étapes critiques.

A. Verrouiller les Workflows (GitHub Actions)

La faille la plus courante réside dans une mauvaise gestion des permissions.

  1. Bannir les permissions excessives : Par défaut, le GITHUB_TOKEN a souvent des droits d’écriture (write). Forcez le mode “moindre privilège” au niveau de l’organisation ou du fichier YAML.

    # Bonne pratique : Déclarer les permissions au top-level
    permissions:
      contents: read # Lecture du code uniquement
      pull-requests: read
    
  2. Attention au pull_request_target : Ne combinez JAMAIS le trigger pull_request_target avec un checkout du code de la PR suivi d’un run: npm install. C’est une porte ouverte à une exécution de code arbitraire avec accès à vos secrets de dépôt.
    • Règle : Utilisez pull_request pour les contributeurs externes (pas d’accès aux secrets).
  3. Audit automatisé avec Zizmor : Il existe un excellent outil open-source nommé zizmor. C’est un linter de sécurité statique pour GitHub Actions.

    Action : Installez-le en local ou dans votre CI pour détecter les injections de scripts et les mauvaises configs.

    cargo install zizmor
    zizmor .github/workflows/
    

B. Signer l’Origine avec Sigstore (Cosign)

Construire une image Docker ne suffit pas. Comment prouver à votre cluster Kubernetes que l’image myapp:latest a bien été construite par votre workflow GitHub et pas par un attaquant qui a volé vos identifiants Docker Hub ?

La réponse est la signature Keyless avec Cosign.

L’idée : GitHub agit comme une autorité de certification (OpenID Connect). Cosign utilise le token d’identité éphémère du build pour signer l’image.

Exemple de Pipeline de Signature :

jobs:
  build-and-sign:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
      id-token: write # CRUCIAL : Permet l'authentification OIDC
    steps:
      - name: Build & Push Docker
        uses: docker/build-push-action@v4
        with:
          push: true
          tags: ghcr.io/moi/app:v1

      - name: Install Cosign
        uses: sigstore/cosign-installer@v3.1.1

      - name: Sign the image
        run: |
          cosign sign --yes \
            -a "repo=$" \
            -a "workflow=$" \
            ghcr.io/moi/app:v1

Note : Le flag –yes active le mode sans clé (Keyless). Le certificat est stocké dans le registre public Rekor (transparence).

Sécuriser l’Infrastructure (Runners)

L’infrastructure d’exécution est souvent le maillon faible.

Public vs Self-Hosted : Pour les projets open-source, évitez les “Self-Hosted Runners” persistants. Si un attaquant les compromet, il persiste sur votre machine.

Éphémère est la clé : Un runner doit naître pour un job et mourir immédiatement après.

Solution Kubernetes : Si vous hébergez vos runners, utilisez ARC (Actions Runner Controller). Il spinne des pods Kubernetes à la demande pour chaque job et les détruit après. Impossible pour un attaquant de laisser une porte dérobée persistante sur le runner.

Contrôle de Conformité (Policy as Code)

Ne faites pas confiance, vérifiez. Utilisez Open Policy Agent (OPA) ou Kyverno pour scanner vos configurations avant le déploiement.

Exemple de politique OPA (Rego) : “Bloquer tout déploiement si l’image Docker ne possède pas de signature Cosign valide provenant de notre repo GitHub.”

Conclusion

Sécuriser sa chaîne de build demande un changement de mentalité. Il ne s’agit plus seulement de produire du code, mais de garantir l’intégrité de la production.

En auditant vos workflows avec zizmor, en signant vos artefacts avec Cosign, et en isolant vos runners, vous élevez le coût de l’attaque de manière exponentielle pour les hackers.

Ressources techniques :

Documentation Sigstore/Cosign

Zizmor - Audit Tool for GitHub Actions

Securing GitHub Actions (GitHub Docs)