Changer le thème

Secrets GitHub Actions : du risque de fuite au déploiement OIDC sans clé

Easton editorial illustration: modular system blueprint

Un week-end de mars 2025, l’équipe sécurité de GitHub a envoyé un e-mail urgent à plus de 23 000 propriétaires de dépôts.

Leurs secrets avaient peut-être fuité.

Le coupable : l’action tj-actions/changed-files — un outil très utilisé, compromis par des attaquants qui volaient discrètement toutes les variables d’environnement et les secrets du workflow. Vous vous demandez peut-être : mon projet l’utilise aussi, suis-je concerné ?

Cet incident a mis sur la table une question que beaucoup ignorent : comment gérer correctement les secrets dans GitHub Actions ?

Dans cet article, nous abordons trois sujets : comment choisir l’architecture à trois niveaux des secrets, comment appliquer les 8 règles de sécurité, et comment OIDC vous permet d’échapper au cauchemar des credentials statiques qui fuient.

1. L’architecture à trois niveaux des Secrets GitHub Actions

GitHub propose trois niveaux pour stocker les secrets : Repository, Environment et Organization. Lequel choisir ? La réponse dépend de votre scénario.

Repository Secrets : le choix des projets personnels

C’est le niveau le plus simple. Les secrets vivent au niveau du dépôt ; tous les workflows y accèdent. Projet perso, dépôt monolithique, pas de déploiement multi-environnement — ce niveau suffit en général.

Seul inconvénient : impossible de distinguer un secret homonyme entre staging et production. Par exemple un DATABASE_URL différent selon l’environnement — là, il faut Environment Secrets.

Environment Secrets : indispensable pour le multi-environnement

Les secrets d’environnement s’isolent par environnement et supportent des flux d’approbation. Dans les paramètres du dépôt, créez par exemple staging et production, chacun avec ses secrets.

Point clé : seul le job qui référence cet environnement accède aux secrets correspondants. Les frontières de sécurité sont plus nettes.

jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    environment: staging  # référence l'environnement staging
    steps:
      - run: echo "Deploying to staging..."
      - env:
          API_KEY: ${{ secrets.API_KEY }}  # secret de l'environnement staging

  deploy-production:
    runs-on: ubuntu-latest
    environment: production  # référence production (approbation possible)
    steps:
      - run: echo "Deploying to production..."
      - env:
          API_KEY: ${{ secrets.API_KEY }}  # secret de l'environnement production

Dans cette config, deploy-staging n’accède qu’aux secrets de staging, et deploy-production qu’à ceux de production. Aucune interférence entre les deux.

Les Environment supportent aussi des règles de protection — par exemple exiger une approbation manuelle avant d’exécuter production. Très utile en équipe.

Organization Secrets : partage d’équipe, gestion centralisée

Votre équipe a des dizaines de dépôts, chacun avec le même AWS_ACCESS_KEY — copier-coller des dizaines de fois, puis tout refaire à chaque rotation : épuisant.

Les Organization secrets règlent ça : une configuration au niveau organisation, réutilisable par tous les dépôts. Vous contrôlez l’accès : tous les dépôts, ou une liste ciblée.

# Dans le workflow du dépôt, même syntaxe que les repository secrets
steps:
  - env:
      AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
      AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

Comment choisir ?

En résumé :

ScénarioNiveau recommandé
Projet perso, environnement uniqueRepository Secrets
Déploiement multi-environnement (staging/production)Environment Secrets
Équipe, plusieurs dépôts, credentials partagésOrganization Secrets

D’après mon expérience, beaucoup de projets démarrent avec Repository Secrets puis migrent vers Environment Secrets quand le multi-env arrive — la migration reste légère, mais mieux vaut anticiper.

2. Bonnes pratiques de sécurité des Secrets — 8 règles

Nous avons vu où stocker les secrets ; voyons comment les utiliser. Voici 8 règles tirées du terrain, chacune avec sa leçon.

1. Nommage cohérent

MAJUSCULES + underscores, par ex. AWS_ACCESS_KEY_ID. Pas de minuscules, pas de camelCase. On reconnaît immédiatement un secret, sans confusion avec une variable ordinaire.

2. Ne pas stocker un JSON entier dans un seul secret

Piège classique : tout un fichier de config dans un secret :

{"api_key": "xxx", "db_url": "yyy", "token": "zzz"}

Puis parsing avec fromJson dans le workflow. Si ce secret fuit, tout part ensemble. Mieux : une valeur par secret.

3. Passage explicite, pas d’inlining

# Mauvais ❌
- run: my-cli --token ${{ secrets.MY_TOKEN }}

# Bon ✅
- env:
    MY_TOKEN: ${{ secrets.MY_TOKEN }}
  run: my-cli --token $MY_TOKEN

Pourquoi ? Selon GitGuardian, les arguments de ligne de commande sont visibles par d’autres processus sur la même machine via ps x -w. Les variables d’environnement sont nettement plus sûres.

4. Rotation régulière

Tous les 30 à 90 jours. La rotation est pénible — mais bien moins que la réponse à incident après fuite. L’équipe Blacksmith recommande, avec AWS/GCP, d’envisager OIDC pour éviter cette corvée.

5. Épingler les Actions par SHA

Première ligne de défense contre la supply chain.

# Mauvais ❌
- uses: tj-actions/changed-files@v45

# Bon ✅
- uses: tj-actions/changed-files@b827595e0a7e97537d7c7a2f458b5a8e6d5c8e39

Utilisez le SHA du commit, pas un tag de version. Un tag peut être détourné ; un SHA est immuable.

6. GITHUB_TOKEN avec le moindre privilège

GitHub fournit automatiquement un GITHUB_TOKEN par workflow. Par défaut, les droits sont trop larges — écriture de code, modification d’issues. Passez-le en read-only dans le workflow ou les paramètres du dépôt :

permissions:
  contents: read

7. Vérifier le masquage des secrets dans les logs

GitHub remplace ${{ secrets.XXX }} par *** dans les logs. Mais si vous écrivez :

- run: echo "Token is $MY_TOKEN"

la valeur réelle peut apparaître. Testez vos workflows pour confirmer l’absence d’exposition accidentelle.

8. Enregistrer les valeurs sensibles dérivées

Si le workflow génère une nouvelle donnée sensible à partir d’un secret (par ex. un JWT à partir d’une clé API), enregistrez-la aussi comme secret — ne la faites pas transiter uniquement en mémoire.


Ces 8 règles ne sont pas théoriques : après tj-actions, l’équipe StepSecurity a audité des milliers de dépôts publics — une part significative en violait plusieurs. Les corriger demande du temps, mais reste faisable.

3. OIDC — authentification cloud sans secrets stockés

Les règles ci-dessus évoquent la rotation. Honnêtement, c’est fastidieux — modifier la console AWS, mettre à jour les secrets GitHub, prévenir l’équipe.

OIDC (OpenID Connect) ouvre une autre voie : ne plus stocker de secrets du tout.

Comment fonctionne OIDC ?

Méthode classique : créer un utilisateur IAM AWS, générer des access keys, les mettre dans les secrets GitHub. À chaque run, le workflow utilise ces clés statiques.

Méthode OIDC : GitHub agit comme fournisseur d’identité et prouve à AWS que « ce workflow vient du dépôt eastondev/my-repo ». AWS valide et délivre un JWT court (quelques minutes à quelques heures). Le workflow l’utilise puis le jeton expire.

Pas de credential longue durée, pas de rotation, pas de risque de fuite de clé statique.

Exemple de configuration OIDC sur AWS

Deux parties : confiance côté AWS, demande de jeton côté GitHub.

Côté AWS (console) :

  1. Créer un IAM Identity Provider, URL https://token.actions.githubusercontent.com
  2. Créer un IAM Role avec une politique de confiance limitée à votre dépôt :
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "Federated": "arn:aws:iam::123456789:oidc-provider/token.actions.githubusercontent.com"
    },
    "Action": "sts:AssumeRoleWithWebIdentity",
    "Condition": {
      "StringEquals": {
        "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
        "token.actions.githubusercontent.com:sub": "repo:eastondev/my-repo:ref:refs/heads/main"
      }
    }
  }]
}

Seuls les workflows du dépôt eastondev/my-repo sur la branche main peuvent assumer ce rôle.

Côté GitHub (workflow) :

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      id-token: write  # requis pour demander le jeton OIDC
      contents: read
    steps:
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789:role/GitHubActionsRole
          aws-region: us-east-1
      - run: aws s3 sync ./dist s3://my-bucket

Aucun secrets.AWS_ACCESS_KEY — authentification directe via le rôle.

GCP et Azure

Les trois grands clouds supportent OIDC, avec des variantes similaires :

PlateformeAction GitHubMots-clés doc officielle
AWSaws-actions/configure-aws-credentialsOIDC federation
GCPgoogle-github-actions/authWorkload Identity Federation
Azureazure/loginFederated Identity Credentials

Un avantage inattendu d’OIDC

Selon des tests sur johal.in, la latence d’authentification OIDC est environ 87 % plus basse qu’avec des secrets classiques : pas d’appel à l’API des secrets GitHub, échange local du JWT contre un jeton temporaire.

Pour le déploiement cloud, OIDC est mon choix par défaut. La configuration est un peu plus lourde au départ — ensuite, plus rien à maintenir côté credentials.

4. Protection supply chain — retour sur l’incident tj-actions

Revenons à tj-actions/changed-files. Comment c’est arrivé ? Qu’en tirer ?

Déroulement

En mars 2025, des attaquants ont obtenu les droits de mainteneur sur le dépôt tj-actions (fuite de credentials ou compromission de compte — enquête en cours). Dans la v45, un script malveillant lisait toutes les variables d’environnement et secrets, puis les envoyait vers un serveur contrôlé par l’attaquant.

L’analyse Semgrep indique que tous les workflows utilisant tj-actions/changed-files@v45 étaient touchés — secrets GitHub ou OIDC : dès que l’action voit les variables d’environnement, elles peuvent être exfiltrées.

Le rapport Unit42 (Palo Alto) cite plus de 23 000 dépôts utilisant cette action, dont des forks de projets connus.

Leçons

L’incident a mis en lumière :

  1. Les tags de version d’Actions ne sont pas fiables — un attaquant peut faire pointer un tag vers un commit malveillant
  2. Une Action tierce accède à vos secrets — une compromission expose tout
  3. Les logs historiques peuvent garder des traces — même après correction, d’anciens runs peuvent contenir des fuites

Votre checklist

Si vous avez déjà utilisé tj-actions/changed-files, parcourez point par point :

□ Vérifier les logs workflow : pas de secrets dans la sortie
□ Rotation de tous les secrets potentiellement exposés (API keys, tokens, etc.)
□ Épingler l'action sur un SHA de commit, pas sur un numéro de version
□ Auditer la provenance des mainteneurs des autres Actions tierces
□ Envisager Dependabot ou Renovate pour surveiller les mises à jour d'Actions

Pour les projets non encore compromis, le pinning SHA est la mesure la plus importante. Moins lisible qu’un tag, mais seule protection fiable contre la modification de tag.

Par ailleurs, la feuille de route sécurité GitHub 2026 évoque une direction importante : séparer les droits de contribution au code et la gestion des credentials. Des contrôles plus fins pourraient limiter les secrets accessibles aux Actions tierces — bonne nouvelle, mais pour l’instant la défense reste entre vos mains.

Conclusion

La gestion des Secrets GitHub Actions n’est pas un problème technique insurmontable — c’est une pratique de sécurité à entretenir. En résumé :

Architecture : Repository Secrets pour le perso, Environment Secrets pour le multi-env, Organization Secrets pour le partage d’équipe.

Sécurité : épinglage SHA, passage explicite, rotation régulière — les trois priorités.

Cloud : OIDC en premier choix — pas de stockage de secrets, pas de rotation de clés statiques.

Supply chain : limiter l’accès des Actions tierces aux secrets, auditer les mainteneurs.

Après tj-actions, ma routine : toutes les Actions épinglées par SHA, déploiements cloud en OIDC, audit mensuel des secrets. Deux semaines pour mettre en place, puis faible coût de maintenance.

Si vous n’avez pas encore fait ces vérifications, lancez la checklist de cet article aujourd’hui. Prévenir coûte bien moins cher que réparer.

Configurer un déploiement GitHub Actions OIDC sans clé

Configurer l'authentification OIDC pour AWS et déployer sans stocker de credentials statiques

⏱️ Estimated time: 30 min

  1. 1

    Step 1: Créer un IAM Identity Provider

    Dans la console IAM AWS, créez un fournisseur d'identité :

    • Provider URL : https://token.actions.githubusercontent.com
    • Audience : sts.amazonaws.com
    • Notez l'ARN du Provider généré
  2. 2

    Step 2: Créer un IAM Role et la politique de confiance

    Créez un IAM Role limité à votre dépôt GitHub :

    ```json
    {
    "Version": "2012-10-17",
    "Statement": [{
    "Effect": "Allow",
    "Principal": {
    "Federated": "arn:aws:iam::ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com"
    },
    "Action": "sts:AssumeRoleWithWebIdentity",
    "Condition": {
    "StringEquals": {
    "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
    "token.actions.githubusercontent.com:sub": "repo:OWNER/REPO:ref:refs/heads/main"
    }
    }
    }]
    }
    ```

    • Remplacez ACCOUNT_ID par votre ID de compte AWS
    • Remplacez OWNER/REPO par votre dépôt GitHub
    • Vous pouvez retirer :ref:refs/heads/main pour autoriser toutes les branches
  3. 3

    Step 3: Configurer le workflow pour OIDC

    Demandez un jeton OIDC dans le workflow GitHub Actions :

    ```yaml
    jobs:
    deploy:
    runs-on: ubuntu-latest
    permissions:
    id-token: write # requis pour le jeton OIDC
    contents: read
    steps:
    - uses: aws-actions/configure-aws-credentials@v4
    with:
    role-to-assume: arn:aws:iam::ACCOUNT_ID:role/GitHubActionsRole
    aws-region: us-east-1
    - run: aws s3 sync ./dist s3://my-bucket
    ```

    • id-token: write est une permission obligatoire
    • Aucun secrets.AWS_ACCESS_KEY_ID n'est nécessaire
  4. 4

    Step 4: Tester et valider

    Exécutez le workflow pour valider OIDC :

    • Vérifiez dans les logs que l'authentification réussit
    • Confirmez que role-to-assume est correct
    • Validez l'accès aux ressources AWS sans credential statique
    • Testez l'opération cible (sync S3, push ECR, etc.)

FAQ

Quelle différence entre Repository Secrets et Environment Secrets ?
Les Repository Secrets sont au niveau dépôt : tous les workflows y accèdent. Les Environment Secrets sont isolés par environnement : seul le job qui référence cet environnement accède aux secrets correspondants, avec possibilité d'approbation. Pour le multi-environnement, préférez Environment Secrets.
Comment utiliser les secrets en toute sécurité dans un workflow ?
Trois principes :

• Passage explicite : injecter via env, ne pas inliner ${{ secrets.XXX }}
• Moindre privilège : ne passer qu'aux steps nécessaires ; pour GITHUB_TOKEN, permissions: contents: read
• Rotation régulière : tous les 30-90 jours ; pour le cloud, OIDC évite la rotation de clés statiques
Qu'est-ce que OIDC ? Pourquoi est-ce plus sûr que les secrets classiques ?
OIDC (OpenID Connect) permet à GitHub de prouver l'identité du workflow au cloud, qui délivre un JWT court. Avantages : pas de credential statique stocké, pas de rotation de clés, pas de risque de fuite de secret longue durée. La latence d'authentification est environ 87 % plus basse qu'avec les secrets intégrés.
Comment se protéger des attaques supply chain (comme tj-actions) ?
Mesures clés :

• Épingler les Actions sur le SHA du commit, pas sur un tag de version
• Limiter les secrets aux seules Actions de confiance
• Auditer la provenance des mainteneurs des Actions tierces
• Faire tourner Dependabot ou Renovate pour surveiller les mises à jour
Les secrets peuvent-ils apparaître dans les logs ?
GitHub remplace automatiquement ${{ secrets.XXX }} par *** dans les logs. En revanche, echo $MY_TOKEN ou équivalent peut afficher la valeur réelle. Testez vos logs de workflow pour détecter toute exposition accidentelle.
Dans quels cas utiliser Organization Secrets ?
Pour une équipe avec plusieurs dépôts partageant les mêmes credentials : une configuration au niveau organisation, réutilisable partout. Accès configurable (tous les dépôts ou liste ciblée). Exemple : plusieurs projets partageant AWS_ACCESS_KEY sans duplication par dépôt.

8 min de lecture · Publié le: 18 avr. 2026 · Mis à jour le: 27 juil. 2026

Commentaires

Connectez-vous avec GitHub pour laisser un commentaire

Easton BlogEaston Blog