Chapitre 721 minIntermédiaire
Sécurité dans la CI : SAST, dépendances et secrets (DevSecOps)
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- distinguer SAST, SCA, DAST et détection de secrets ;
- mettre en place CodeQL, la revue des dépendances et Dependabot ;
- expliquer les attaques qui visent un pipeline et leurs contre-mesures ;
- éviter le piège de pull_request_target avec les forks.
4 exercices corrigés
DevSecOps consiste à intégrer la sécurité à chaque étape du cycle de développement, en automatisant des contrôles dans le pipeline : analyse du code source (SAST), analyse des dépendances (SCA), détection des secrets, analyse des images et tests de l'application en fonctionnement (DAST). Le pipeline doit aussi être protégé lui-même, car il détient les secrets de déploiement. Ce chapitre présente chaque contrôle avec sa mise en place sur GitHub et GitLab, puis les attaques qui visent les pipelines et leurs contre-mesures.
Décaler la sécurité à gauche
Sur une frise qui va du code à la production, « à gauche » signifie « plus tôt ». Une faille trouvée par un outil pendant la pull request coûte quelques minutes à corriger ; la même faille découverte en production coûte un incident, une correction en urgence, parfois une notification à la CNIL. Décaler à gauche (shift left), c'est placer chaque contrôle le plus tôt possible.
| Moment | Contrôle | Exemple d'outil |
|---|---|---|
| Sur le poste, avant le commit | Lint de sécurité, détection de secrets | Extension de l'éditeur, crochet pre-commit |
Au push | Blocage des secrets connus | Push protection de GitHub |
| Dans chaque pull request | Analyse du code (SAST), revue des dépendances ajoutées (SCA) | CodeQL, dependency-review-action, Semgrep, SonarQube |
Sur main et chaque nuit | Audit complet des dépendances, analyse de l'image | npm audit, Dependabot, Trivy, Grype |
| En recette | Test dynamique de l'application qui tourne (DAST) | OWASP ZAP |
| En production | Surveillance, journaux, alertes | Supervision, SIEM |
Analyser le code source : SAST
Le SAST (Static Application Security Testing) lit le code sans l'exécuter et cherche des motifs dangereux : une requête SQL construite par concaténation, un eval sur une saisie, un mot de passe en dur, une désérialisation risquée. Il suit le chemin des données, de la saisie de l'utilisateur jusqu'à l'endroit sensible.
Sur GitHub, l'outil intégré est CodeQL, gratuit pour les dépôts publics. Le plus simple est la configuration par défaut : Settings, Code security, CodeQL analysis, Set up, Default. Pour la régler finement, on écrit le workflow :
name: CodeQL
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '17 3 * * 1' # chaque lundi, pour profiter des nouvelles règles
permissions:
contents: read
security-events: write # publier les alertes dans l'onglet Security
jobs:
analyse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: github/codeql-action/init@v4
with:
languages: javascript-typescript
- uses: github/codeql-action/analyze@v4
Les résultats apparaissent comme des alertes dans l'onglet Security, Code scanning, et en commentaire sur les lignes concernées de la pull request : nature de la faille, chemin de la donnée, gravité, conseil de correction. Chaque alerte se traite : on corrige, ou on la ferme en justifiant (faux positif, code de test).
D'autres outils jouent le même rôle : Semgrep (règles lisibles, nombreux langages), SonarQube (qualité et sécurité), PHPStan avec des extensions de sécurité pour PHP, les analyseurs de sécurité de .NET.
Analyser les dépendances : SCA
Une API Node.js de 2 000 lignes embarque souvent plus de 500 paquets, écrits par d'autres. Le SCA (Software Composition Analysis) compare la liste de tes dépendances, directes et indirectes, aux bases de vulnérabilités connues (CVE, avis de sécurité de GitHub) (dépendances et chaîne d'approvisionnement).
Dans le pipeline, chaque écosystème a sa commande d'audit, qui renvoie un code d'erreur quand elle trouve une faille au-dessus du seuil :
| Écosystème | Commande |
|---|---|
| Node.js | npm audit --audit-level=high (échoue à partir d'une faille de gravité haute) |
| PHP | composer audit |
| .NET | dotnet restore avec NuGet Audit (actif par défaut) et <WarningsAsErrors>NU1903;NU1904</WarningsAsErrors> dans le projet pour échouer sur les failles hautes et critiques ; liste détaillée : dotnet package list --vulnerable --include-transitive |
| Python | pip-audit |
Sur les pull requests, l'action officielle dependency-review-action examine uniquement les dépendances ajoutées ou modifiées par la pull request et la bloque si l'une d'elles a une faille connue :
name: Revue des dépendances
on:
pull_request:
permissions:
contents: read
jobs:
revue:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/dependency-review-action@v5
with:
fail-on-severity: high
Elle est gratuite pour les dépôts publics ; pour les dépôts privés, elle demande l'option de sécurité payante de GitHub.
En continu, Dependabot, intégré à GitHub, fait trois choses : il t'alerte quand une dépendance a une faille connue, il ouvre une pull request de correction de sécurité, et, si tu l'as configuré, il ouvre des pull requests de mise à jour régulières, que ton pipeline teste aussitôt. Active les deux premières dans Settings, Code security, et décris la troisième dans .github/dependabot.yml :
version: 2
updates:
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
groups:
outils-de-dev:
dependency-type: development # une seule pull request pour les outils de développement
- package-ecosystem: github-actions # les versions des actions de tes workflows
directory: /
schedule:
interval: monthly
- package-ecosystem: docker # l'image de base du Dockerfile
directory: /
schedule:
interval: weekly
Sur GitLab, l'équivalent de Dependabot est l'outil libre Renovate, qui fonctionne aussi sur GitHub.
Détecter les secrets
Un mot de passe, une clé d'API ou une clé SSH commités dans un dépôt sont compromis, même si le commit suivant les supprime : ils restent dans l'historique, et des robots parcourent en permanence les dépôts publics à leur recherche.
- Secret scanning (GitHub) analyse les dépôts publics et reconnaît des centaines de formats de jetons (GitHub, AWS, Azure, Google Cloud, Stripe, Slack…). Pour certains fournisseurs, il les signale directement à l'émetteur, qui peut les révoquer.
- Push protection va plus loin : il refuse le
pushqui contient un secret reconnu, avant qu'il n'atteigne le dépôt. Elle est activée par défaut pour les dépôts publics. - Gitleaks et TruffleHog, outils libres, cherchent les secrets dans l'historique, en local ou dans n'importe quelle CI ; GitLab intègre sa propre détection (Secret Detection).
Si un secret a fuité, l'ordre des actions compte : d'abord révoquer le secret et en créer un nouveau, ensuite seulement nettoyer l'historique (un secret a fuité dans Git). Le chapitre 8 explique comment stocker les secrets du pipeline.
Analyser les images de conteneurs
Une image Docker contient un système de base (Debian, Alpine) et ses paquets, en plus de ton application. Un scanner d'images (Trivy, Grype, Docker Scout) liste ces paquets et les compare aux bases de vulnérabilités. On le lance dans le pipeline après la construction de l'image, avec un seuil : échouer s'il trouve une faille critique pour laquelle un correctif existe.
Trois habitudes réduisent les alertes à la source (chapitre 9) :
- partir d'une image de base minimale (
node:24-slim, une image distroless) : moins de paquets, moins de failles ; - reconstruire régulièrement l'image, même sans changement du code, pour récupérer les correctifs du système de base ;
- laisser Dependabot proposer les nouvelles versions de l'image de base.
Tester l'application qui tourne : DAST
Le DAST (Dynamic Application Security Testing) teste l'application en fonctionnement, de l'extérieur, comme le ferait un attaquant : il parcourt les pages, envoie des requêtes, observe les réponses, et signale les en-têtes de sécurité manquants, les cookies sans attribut Secure, les messages d'erreur trop bavards, certaines injections. L'outil libre de référence est OWASP ZAP.
On le lance dans la CI contre l'environnement de recette, juste après le déploiement, en mode passif (baseline) à chaque déploiement et en mode actif plus rarement, jamais contre la production pendant les heures d'activité.
La sécurité du pipeline lui-même
Le pipeline a accès au code, aux secrets de déploiement et parfois à la production : c'est une cible de choix. Les attaques suivantes ont toutes eu lieu sur de vrais projets.
Une action tierce compromise. En mars 2025, un attaquant a pris le contrôle de l'action très utilisée tj-actions/changed-files et a déplacé ses étiquettes de version vers un commit malveillant, qui affichait les secrets des workflows dans leurs journaux (CVE-2025-30066). Un an plus tard, en mars 2026, des dizaines d'étiquettes de l'action aquasecurity/trivy-action, un scanner de sécurité, ont été réécrites de la même façon pour voler les secrets des pipelines qui l'utilisaient (CVE-2026-33634). Les dépôts qui référençaient ces actions par une étiquette (@v1) ont exécuté le code piégé ; ceux qui les référençaient par une empreinte de commit n'ont pas été touchés.
Contre-mesures :
- épingler les actions tierces sur l'empreinte complète d'un commit, avec la version en commentaire pour rester lisible ; Dependabot met à jour ces empreintes ;
- limiter les actions autorisées (Settings, Actions, General : seulement les actions de GitHub, des créateurs vérifiés et une liste choisie) ; GitHub permet aussi d'imposer l'épinglage par empreinte au niveau du dépôt ou de l'organisation ;
- préférer une commande de quelques lignes à une action tierce qui fait la même chose.
# Action tierce épinglée sur un commit précis (exemple d'empreinte, ne la recopie pas)
- uses: exemple-editeur/action-deploiement@9f2c5e1a7b3d4c6e8f0a1b2c3d4e5f6a7b8c9d0e # v2.4.1
L'injection de script. Une donnée choisie par un inconnu (titre de pull request, nom de branche, message de commit) insérée avec ${{ }} dans un run devient du code. Parade : passer par une variable d'environnement (expressions et contextes).
Des droits trop larges. Un workflow qui n'a besoin que de lire le code n'a aucune raison de pouvoir écrire dans le dépôt ou publier un paquet. Déclare permissions: contents: read en tête de chaque workflow, et ajoute job par job les seuls droits nécessaires. Dans les réglages du dépôt, laisse la permission par défaut du jeton en lecture seule (Read repository contents and packages permissions).
L'empoisonnement du cache. Un job peu sûr enregistre un cache modifié, qu'un job privilégié restaure ensuite (le cache). Parade : pas de cache dans les workflows de publication et de déploiement quand il n'est pas indispensable.
Les modifications de workflow. Celui qui modifie un workflow choisit ce qui s'exécute avec les secrets. Protège .github/workflows/ avec CODEOWNERS et la revue obligatoire (protection de branche).
pull_request_target et les forks
Quand quelqu'un propose une pull request depuis un fork (sa copie du dépôt), le déclencheur pull_request exécute le workflow sans les secrets et avec un jeton en lecture seule : un inconnu ne peut rien voler en modifiant le code ou le workflow.
Le déclencheur pull_request_target, lui, s'exécute dans le contexte du dépôt cible, avec ses secrets et un jeton en écriture. Il sert à des tâches qui ne touchent pas au code proposé, comme étiqueter la pull request ou poster un message de bienvenue. Le danger apparaît quand un tel workflow récupère et exécute le code de la pull request (installation des dépendances, tests) : l'inconnu fait alors exécuter son propre code avec les secrets du dépôt. Cette erreur, surnommée pwn request, a touché de nombreux projets libres.
Depuis juin 2026, actions/checkout (version 7, et 6.1 par rétroportage) refuse par défaut de récupérer le code d'une pull request de fork dans un workflow déclenché par pull_request_target ou workflow_run ; il faut une option explicite (allow-unsafe-pr-checkout) pour passer outre. Les versions plus anciennes n'ont pas ce garde-fou, raison de plus pour laisser Dependabot tenir tes actions à jour. La règle reste simple : pour lancer les tests d'une pull request, utilise pull_request, jamais pull_request_target.
Avec GitLab
GitLab fournit des modèles de jobs de sécurité, que l'on inclut en une ligne :
include:
- template: Jobs/SAST.gitlab-ci.yml
- template: Jobs/Secret-Detection.gitlab-ci.yml
stages:
- test
Les jobs ajoutés détectent les langages du projet et produisent un rapport, téléchargeable comme artefact. L'analyse des dépendances, l'analyse des images et le DAST existent aussi sous forme de modèles ; l'affichage des résultats dans la merge request et le tableau de bord des vulnérabilités sont réservés à l'offre la plus haute de GitLab. Les mêmes outils libres (Semgrep, Trivy, Gitleaks, ZAP) se lancent aussi par de simples commandes dans un script.
Le lien avec l'E7 SLAM
La compétence du bloc 3 SLAM « Assurer la cybersécurité d'une solution applicative et de son développement » a deux indicateurs que la CI prouve directement : « les composants utilisés sont certifiés, sécurisés et actualisés » (Dependabot, npm audit, revue des dépendances) et « des tests de sécurité sont prévus et mis en œuvre » (SAST, DAST, tests d'autorisation automatisés). Dans une copie d'E7, quand on te demande comment éviter qu'une faille de dépendance revienne, la réponse attendue est un contrôle automatique dans le pipeline, avec son seuil et son moment (les compétences du bloc 3 évaluées à l'E7, tests de sécurité à l'E7, méthode de l'E7 SLAM).
Exercices
Exercice 1 sur 4 SAST, SCA, DAST ou secrets ? Réussi À revoir
Classe chaque contrôle.
- Un outil signale que la ligne 42 de
notes.controller.jsconstruit une requête SQL en concaténantreq.query.tri. - Un outil signale que la version de la bibliothèque de génération de PDF a une faille connue de gravité haute.
- Un outil, lancé contre
https://recette.sorbier.example, signale que l'en-têteContent-Security-Policyest absent. - GitHub refuse un
pushqui contient une clé d'API d'un service de paiement. - Un outil signale que l'image Docker contient une version d'OpenSSL vulnérable.
Voir la réponseMasquer la réponse de l'exercice 1
- SAST : analyse du code source, sans l'exécuter.
- SCA : analyse des dépendances.
- DAST : test de l'application en fonctionnement.
- Détection de secrets, ici la push protection.
- Analyse d'image de conteneur : c'est une forme de SCA appliquée aux paquets du système de l'image.
Tu avais trouvé ?
Exercice 2 sur 4 Lire un audit de dépendances Réussi À revoir
L'étape npm audit --audit-level=high échoue. Voici la fin de sa sortie (extrait simplifié, paquets fictifs) :
# npm audit report
pdf-generateur <3.2.1
Severity: high
Prototype Pollution in pdf-generateur
fix available via `npm audit fix`
node_modules/pdf-generateur
date-format-lite <1.4.0
Severity: moderate
Regular Expression Denial of Service in date-format-lite
fix available via `npm audit fix --force`
Will install date-format-lite@2.0.0, which is a breaking change
node_modules/date-format-lite
2 vulnerabilities (1 moderate, 1 high)
- Pourquoi l'étape échoue-t-elle ? Échouerait-elle avec
--audit-level=critical? - Que proposes-tu pour chaque paquet ?
- Pourquoi ne pas ajouter simplement
npm audit fix --forcedans le pipeline ?
Voir la réponseMasquer la réponse de l'exercice 2
- Une faille de gravité haute (
pdf-generateur) atteint le seuilhigh. Avec--audit-level=critical, aucune faille n'atteint le seuil : l'étape réussirait. pdf-generateur:npm audit fixinstalle une version corrigée compatible (3.2.1 ou plus, même version majeure) ; on le fait dans une branche, on vérifie que les tests passent, et la pull request est testée par la CI.date-format-lite: le correctif impose la version majeure 2.0.0, qui peut casser l'API ; on lit son journal des modifications, on adapte le code si besoin, on teste. En attendant, la faille est modérée : on la note dans un ticket avec une échéance.- Le pipeline modifierait les dépendances sans revue, en installant des versions majeures non testées, et ces modifications ne seraient même pas commitées : le code livré ne correspondrait plus au dépôt. La CI détecte ; la correction passe par une pull request.
Tu avais trouvé ?
Exercice 3 sur 4 Un workflow dangereux Réussi À revoir
Ce workflow a été ajouté pour « tester les contributions externes avec la base de recette » :
on: pull_request_target
jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
ref: ${{ github.event.pull_request.head.sha }}
allow-unsafe-pr-checkout: true
- run: npm ci
- run: npm test
env:
DB_PASSWORD: ${{ secrets.RECETTE_DB_PASSWORD }}
- Explique le risque, sans décrire d'attaque précise.
- Propose une correction.
Voir la réponseMasquer la réponse de l'exercice 3
pull_request_targets'exécute avec les secrets du dépôt et un jeton en écriture. Le workflow récupère le code proposé par l'auteur de la pull request, qui peut être n'importe qui, puis l'exécute (npm ciexécute les scripts d'installation des paquets,npm testle code des tests). Un contributeur malveillant ferait donc exécuter son propre code avec le mot de passe de la base de recette dans l'environnement. L'optionallow-unsafe-pr-checkout: truedésactive justement la protection ajoutée paractions/checkout.- Utiliser
on: pull_request: les pull requests de forks s'exécutent alors sans secrets. Les tests utilisent une base jetable créée dans le job (services: mysql), pas la base de recette. Si des tests ont vraiment besoin d'un secret, ils ne tournent qu'après la fusion, surmain, ou après validation par un mainteneur.
Tu avais trouvé ?
Exercice 4 sur 4 Configurer Dependabot Réussi À revoir
Écris le fichier .github/dependabot.yml d'une application Symfony dont le dépôt contient aussi des workflows GitHub Actions et un Dockerfile. Les dépendances PHP doivent être vérifiées chaque semaine, le reste chaque mois.
Voir la réponseMasquer la réponse de l'exercice 4
version: 2
updates:
- package-ecosystem: composer
directory: /
schedule:
interval: weekly
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly
- package-ecosystem: docker
directory: /
schedule:
interval: monthly
Pour github-actions, directory: / suffit : Dependabot cherche lui-même dans .github/workflows/. Les alertes et les correctifs de sécurité, eux, s'activent dans les réglages et n'attendent pas le calendrier.
Tu avais trouvé ?