Skip to Content
SécuritéPartie 13 — Secrets & données sensibles13.1 — Ne jamais commiter un secret

Chapitre 13.1 — Ne jamais commiter un secret

⏱️ TL;DR — L’erreur n°1 de gestion des secrets : les committer dans Git. Une clé d’API, un mot de passe de base, un token « juste pour tester » dans un commit, et le secret est exposé — de façon durable, car il reste dans l’historique même si on le supprime ensuite. Les dépôts publics sont moissonnés en continu par des bots : un secret poussé sur un repo public est souvent exploité en minutes. Les défenses : garder les secrets hors du code (variables d’environnement, fichiers ignorés par Git), un .gitignore correct, et un scan de secrets automatisé (pre-commit + CI). Et si un secret a fuité : il est compromis → le révoquer et le régénérer immédiatement, pas juste le retirer du dernier commit.

🎯 Objectifs

  • Comprendre pourquoi un secret commité est exposé durablement (l’historique Git).
  • Garder les secrets hors du dépôt (env, fichiers ignorés) et configurer .gitignore.
  • Détecter les secrets avec un scan automatisé (pre-commit, CI).
  • Réagir correctement à une fuite : révoquer et régénérer (pas seulement supprimer).

Pourquoi c’est si grave (et si fréquent)

Committer un secret est l’erreur la plus banale — et l’une des plus coûteuses. Elle arrive tout le temps : un .env poussé « par accident », une clé « en dur » dans le code pour dépanner, un token dans un fichier de config d’exemple. Trois raisons rendent ça grave :

  1. C’est durable : Git conserve l’historique. Retirer le secret dans un commit suivant ne l’efface pas — il reste dans les commits précédents, accessibles à quiconque a le dépôt (ou un clone, un fork, un backup, un miroir CI).
  2. C’est activement moissonné : des bots scannent en permanence les dépôts publics (GitHub, etc.) à la recherche de secrets (patterns de clés AWS, tokens, etc.). Un secret poussé sur un repo public est souvent détecté et exploité en quelques minutes.
  3. Un secret = un accès : selon sa portée, la fuite peut donner accès à ta base, à un service cloud (factures, ressources), à des données personnelles. L’impact peut être immédiat et lourd.

⚠️ Piège — « Le repo est privé, donc ce n’est pas grave de committer ce secret. » Faux, pour plusieurs raisons : un repo privé peut devenir public (erreur, transfert), être cloné/forké par des personnes qui ne devraient plus y avoir accès, apparaître dans des backups ou des miroirs (CI, artefacts), et son historique circule avec chaque clone. Un secret dans Git est compromis en principe, privé ou non. La règle est absolue : aucun secret dans le dépôt, jamais.

La règle : les secrets vivent hors du code

Le code et les secrets se séparent. Le code va dans Git ; les secrets vivent ailleurs et sont injectés à l’exécution :

  • Variables d’environnement : l’appli lit ses secrets depuis l’environnement (process.env.X, $_ENV, getenv). En local, un fichier ignoré par Git (.env.local) ; en prod, de vraies variables d’environnement (injectées par la plateforme/le déploiement — Serveur Linux).
  • Vault / gestionnaire de secrets : pour les projets sérieux, un coffre dédié (chapitre 13.2).
  • Fichiers d’exemple non secrets : on commite un .env.example avec des valeurs bidon (pour documenter les variables attendues), jamais les vraies.
# .gitignore : ignorer les fichiers de secrets .env .env.local .env.*.local *.pem *.key
# .env.example (COMMITE) : documente les variables, valeurs FICTIVES DATABASE_URL=postgres://user:password@localhost:5432/app STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxx

Le .gitignore doit couvrir tous les fichiers susceptibles de contenir des secrets (.env et variantes, clés .pem/.key, fichiers de config locaux). Rappel des frameworks : Symfony ignore .env.local (secrets locaux) et commite .env (défauts non secrets) — Partie 9 ; Next.js utilise .env.local ignoré — Partie 10.

Détecter avant qu’il ne soit trop tard

L’erreur humaine étant inévitable, on met en place une détection automatique des secrets :

  • Hook pre-commit : un outil de scan de secrets (ex. gitleaks, detect-secrets, trufflehog) qui bloque un commit contenant un motif de secret (clé, token) avant qu’il n’entre dans l’historique. C’est la barrière la plus efficace (le secret n’atteint jamais Git).
  • Scan en CI : rejouer le scan sur les PR/le dépôt pour attraper ce qui serait passé.
  • Détection côté plateforme : certaines plateformes (GitHub secret scanning) alertent quand un pattern de secret connu est poussé.
# Idee : un hook pre-commit qui scanne les secrets bloque le commit fautif # (via un framework de hooks : le commit echoue si un secret est detecte)

💡 Réflexe — Mets en place un scan de secrets en pre-commit dès le début d’un projet : c’est la défense qui empêche le secret d’entrer dans l’historique (bien mieux que de le nettoyer après coup). Complète par un scan en CI. Et adopte le réflexe : un secret ne se tape jamais dans un fichier versionné — il va dans l’environnement / le vault. Si tu hésites (« est-ce un secret ? »), traite-le comme un secret.

Si un secret a fuité : révoquer et régénérer

Le point le plus mal compris. Si un secret s’est retrouvé dans Git (commité, même supprimé depuis), la seule réponse correcte est de le considérer comme compromis et de :

  1. Révoquer / régénérer le secret immédiatement — changer le mot de passe de base, révoquer et recréer la clé d’API/le token. C’est l’action prioritaire : tant que l’ancien secret est valide, il est exploitable par quiconque l’a vu.
  2. Retirer le secret du code (et, si pertinent, purger l’historique — opération lourde et imparfaite, à faire avec prudence ; mais elle ne remplace pas la révocation).
  3. Investiguer : le secret a-t-il été utilisé ? (logs d’accès du service concerné — lien Partie 15.)
Un secret commite (meme retire ensuite) = COMPROMIS. 1. Revoquer / regenerer le secret <- PRIORITE ABSOLUE 2. Retirer du code (+ purge historique si pertinent) 3. Verifier s'il a ete utilise

⚠️ Piège — Croire qu’il suffit de « supprimer le secret dans un nouveau commit » (ou d’un git rm) pour régler le problème. Non : le secret reste dans l’historique (commits précédents, clones, forks, backups déjà faits), et il a peut-être déjà été moissonné. La suppression du fichier ne rend pas le secret sûr — seule sa révocation/régénération le fait. L’ordre est : d’abord régénérer (rendre l’ancien inutile), ensuite nettoyer. Ne jamais inverser.

🎯 Côté attaquant — Les attaquants automatisent la chasse aux secrets : des bots surveillent les pushes publics et testent immédiatement tout secret trouvé (clé cloud → lancer des ressources coûteuses, token → accéder à l’API, mot de passe DB → dumper la base). Le délai entre le push et l’exploitation se compte en minutes. Ils fouillent aussi l’historique complet des dépôts (pas seulement le dernier état), les forks, et les artefacts de CI. Un secret qui a touché Git, même brièvement, doit être considéré comme déjà entre leurs mains.

🧭 Sur FormaCampus — FormaCampus applique une discipline stricte : aucun secret dans Git (.gitignore couvrant .env*, *.pem, *.key ; secrets en variables d’environnement / vault — chapitre 13.2), un .env.example documente les variables avec des valeurs fictives, et un scan de secrets en pre-commit + CI (gitleaks) bloque tout secret avant l’historique. Lorsqu’une clé d’API a autrefois été commitée par erreur, l’équipe a immédiatement révoqué et régénéré la clé (priorité), puis nettoyé — jamais l’inverse. Les accès du service concerné ont été vérifiés pour détecter tout usage frauduleux. La règle « un secret dans Git = grillé » est intériorisée.

✏️ Exercices

Exercice 1 — La mauvaise réaction. Un dev remarque qu’il a poussé un fichier .env avec la clé d’API de production sur le dépôt (public). Il fait un commit qui supprime le fichier et dit « c’est réglé ». Que lui réponds-tu ?

✅ Solution

Ce n’est pas réglé. Supprimer le fichier dans un nouveau commit ne retire pas la clé de l’historique Git (elle reste dans les commits précédents, les clones, forks et backups déjà réalisés), et sur un dépôt public, elle a très probablement déjà été moissonnée par des bots (exploitation en minutes). La clé doit être considérée comme compromise. Actions, dans l’ordre : (1) révoquer et régénérer la clé immédiatement (priorité absolue — rendre l’ancienne inutilisable) ; (2) vérifier les logs du service pour détecter un usage frauduleux (facturation anormale, accès inconnus) ; (3) retirer le secret du code, et éventuellement purger l’historique (avec prudence) — mais cela ne remplace pas la révocation. Enfin, ajouter .env au .gitignore et un scan de secrets en pre-commit pour que ça ne se reproduise pas. « Supprimer le fichier » ≠ « sécuriser le secret ».

Exercice 2 — Sépare code et secrets. Décris comment gérer proprement un mot de passe de base de données pour qu’il ne soit jamais dans Git, en local et en prod.

✅ Solution

Le mot de passe ne doit jamais figurer dans un fichier versionné. En local : le mettre dans un fichier ignoré par Git (ex. .env.local, présent dans .gitignore), lu par l’appli via une variable d’environnement (process.env.DATABASE_PASSWORD / $_ENV). En prod : l’injecter par une vraie variable d’environnement (fournie par la plateforme/le déploiement — Serveur Linux) ou, mieux, via un vault / gestionnaire de secrets (chapitre 13.2). On commite un .env.example documentant la variable DATABASE_PASSWORD= avec une valeur fictive (pour l’équipe), jamais la vraie. On ajoute un scan de secrets en pre-commit pour bloquer toute fuite accidentelle. Ainsi, le code (dans Git) et le secret (dans l’environnement/vault) restent séparés.

🧠 Quiz de révision

1. Pourquoi un secret commité reste-t-il exposé même si on le supprime ?

Parce que Git conserve l’historique : le secret reste dans les commits précédents, et dans tous les clones, forks, backups et miroirs déjà faits. Le retirer dans un nouveau commit ne l’efface pas de l’historique — il faut le considérer comme compromis.

2. « Le repo est privé, donc committer un secret n’est pas grave. » Vrai ?

Non : un repo privé peut devenir public, être cloné/forké, apparaître dans des backups/miroirs de CI, et son historique circule avec chaque clone. Un secret dans Git est compromis en principe, privé ou non. Règle absolue : aucun secret dans le dépôt.

3. Où doivent vivre les secrets, et que commite-t-on à la place ?

Hors du code : dans des variables d’environnement (fichier .env.local ignoré en local, vraies variables/vault en prod) ou un vault. On commite un .env.example avec des valeurs fictives pour documenter les variables attendues — jamais les vraies.

4. Quelle est la première action si un secret a fuité dans Git ?

Le révoquer et le régénérer immédiatement (rendre l’ancien secret inutilisable) — c’est la priorité absolue. Ensuite seulement : retirer du code, éventuellement purger l’historique, et vérifier s’il a été utilisé. Supprimer le fichier ne suffit jamais.

5. Quelle défense empêche un secret d’entrer dans l’historique ?

Un scan de secrets en pre-commit (gitleaks, detect-secrets…) qui bloque le commit contenant un motif de secret avant qu’il n’atteigne Git. Complété par un scan en CI et un .gitignore correct. C’est bien plus efficace que de nettoyer après coup.


Chapitre suivant : Vaults, rotation & chiffrement — où stocker les secrets pour de bon, comment les chiffrer au repos et les faire tourner.

Last updated on