Git et les secrets : pourquoi un mot de passe commité ne disparaît jamais vraiment
Supprimer un fichier ne suffit pas à effacer son contenu de l'historique
Petite vérification
Avant de commencer, vérifiez si vous savez encore réaliser ces 3 actions très simples, sur la séance précédente consacrée aux variables d'environnement et aux secrets :
- définir la variable
ECOLEavecECOLE="BTS SIO", puis afficher sa valeur depuis un shell enfant avecbash -c 'echo "ECOLE = $ECOLE"', avant et aprèsexport ECOLE - exécuter
ls -l /etc/sauvegarde.envet relever les droits ainsi que le propriétaire du fichier - exécuter
history | grep DB_PASSWORDet vérifier qu'aucune ligne ne s'affiche
📌 Réponse attendue
TP pour réfléchir et résoudre des problèmes
Votre défi pour aujourd'hui consiste à retrouver un mot de passe « supprimé » dans l'historique d'un dépôt Git, puis à décider quoi faire pour de bon
Ce TP se fait entièrement en local, dans le terminal de votre serveur Linux : aucun compte GitHub n'est nécessaire. Git doit déjà être configuré (nom et adresse email), comme dans le cours Git.
Le mot de passe Librairie-2025! utilisé dans ce TP est fictif, ainsi que la librairie et
son stagiaire Léo. N'utilisez jamais un vrai mot de passe dans un exercice.
Étape 1 — Construire le dépôt de la librairie
Léo, stagiaire dans la librairie fictive « Pages et Plumes », développe le petit site de l'établissement. Reproduisez son travail : un premier commit avec la page d'accueil, puis un second commit avec la connexion à la base de données.
mkdir -p ~/tp-git-secrets/site-librairie
cd ~/tp-git-secrets/site-librairie
git init
echo '<?php echo "Bienvenue à la librairie Pages et Plumes";' > index.php
git add index.php
git commit -m "Ajoute la page d'accueil"
echo 'DB_HOST=localhost' > .env
echo 'DB_USER=librairie' >> .env
echo 'DB_PASSWORD=Librairie-2025!' >> .env
git add .
git commit -m "Ajoute la connexion à la base de données"
Ensuite :
- Affichez l'historique avec
git log --onelineet relevez le nombre de commits. - Affichez la liste des fichiers modifiés par le dernier commit avec
git show --stat HEAD. Quel fichier n'aurait jamais dû s'y trouver ? - Quelle commande, vue dans le cours Git, aurait permis de repérer l'erreur avant de lancer
git commit?
📌 Réponse attendue
Étape 2 — « Supprimer » le secret
Léo s'aperçoit de son erreur. Il retire le fichier du dépôt, puis committe la suppression :
git rm .env
git commit -m "Retire le fichier .env"
Ensuite :
- Affichez la liste des fichiers du dossier avec
ls -a. Le fichier.envest-il encore là ? - Affichez l'historique avec
git log --onelineet relevez le nombre de commits. - Affichez les modifications de chaque commit qui a touché ce fichier avec
git log -p -- .env. Retrouvez-vous le mot de passe ? - Relevez l'identifiant du commit « Ajoute la connexion à la base de données » (7 premiers
caractères dans
git log --oneline), puis affichez le fichier tel qu'il était à cet instant avecgit show IDENTIFIANT:.env.
📌 Réponse attendue
Étape 3 — Le secret a déjà été copié
Un collègue de Léo r écupère le dépôt pour travailler dessus. Simulez-le avec un clone, réalisé après la suppression de l'étape 2 :
cd ~/tp-git-secrets
git clone site-librairie clone-collegue
cd clone-collegue
Ensuite :
- Affichez la liste des fichiers du clone avec
ls -a. Le fichier.envest-il présent ? - Retrouvez le mot de passe dans l'historique du clone avec
git log -p -- .env. - Léo supprime maintenant entièrement le dépôt d'origine
site-librairie. Le mot de passe est-il encore récupérable ? Où ?
📌 Réponse attendue
Étape 4 — Corriger pour de bon
Retournez dans le dépôt d'origine (cd ~/tp-git-secrets/site-librairie). Léo doit maintenant
traiter le problème dans le bon ordre :
- Quelle est la toute première mesure à prendre face à ce mot de passe, avant même de toucher à Git ?
- Recréez un fichier
.envlocal avec un nouveau mot de passe fort (en passant parnanopour qu'il n'apparaisse pas dans l'historique de votre session), puis créez un fichier.gitignorequi contient la ligne.env. Vérifiez avecgit statusque.envn'apparaît pas, puis avecgit check-ignore -v .envque Git l'ignore bien. - Ajoutez au dépôt un fichier
.env.examplequi liste les 3 variables avec une valeur vide pourDB_PASSWORD, puis committez.gitignoreet.env.exampleen les nommant (sansgit add .). Vérifiez avecgit ls-filesce que Git suit désormais. - Affichez à nouveau
git log -p -- .env. L'ancien mot de passe y est toujours : en quoi n'est-ce plus un problème, si la mesure du point 1 a été appliquée ?
📌 Réponse attendue
Étape 5 — Réagir à un cas pratique
Imaginez que Léo ait déjà envoyé son dépôt sur un dépôt GitHub public il y a dix minutes. Un
collègue propose : « pas de panique, on repasse simplement le dépôt en privé et on supprime le
fichier .env, comme ça le problème est réglé ». Que lui répondez-vous ?
Restitution orale
Un étudiant (ou un binôme) vient présenter au tableau les endroits où le mot de passe était encore lisible aux étapes 2 et 3, les mesures prises à l'étape 4 dans leur ordre, et sa réponse au collègue de l'étape 5.
- Durée : 5 minutes de présentation + 2 à 3 minutes de questions.
- Qui passe : un(e) étudiant(e) (ou un binôme) désigné(e) par l'enseignant.
- Support : le travail réalisé pendant le TP, aucune préparation supplémentaire n'est nécessaire.
- Ce qui est attendu :
- rappeler pourquoi le mot de passe reste lisible après
git rmet après un clone (historique conservé, copies hors de portée) ; - présenter les mesures de l'étape 4 dans l'ordre et justifier cet ordre ;
- répondre aux questions de la classe.
- rappeler pourquoi le mot de passe reste lisible après
Cet exercice entraîne à l'oral technique court exigé par plusieurs épreuves du BTS (notamment E5 et E6) : clarté, structure, capacité à répondre à l'improviste.
📌 Une solution
Notions théoriques
Vous venez de constater qu'un mot de passe commité reste lisible longtemps après avoir été « supprimé ». Pour comprendre pourquoi, il faut regarder comment Git conserve l'historique.
Un commit est un instantané, pas une différence effacée
Chaque commit enregistre l'état complet des fichiers suivis à un instant donné, et pointe vers le commit qui le précède. L'historique est donc une chaîne de commits, dont aucun n'est modifié quand on en ajoute un nouveau.
| Commande | Ce qui se passe réellement |
|---|---|
git rm .env puis git commit | Un nouveau commit est ajouté, dans lequel .env n'existe plus. Les commits précédents contiennent toujours .env. |
git log -p -- .env | Affiche chaque commit qui a touché ce fichier, avec les lignes ajoutées (+) et retirées (-). |
git show IDENTIFIANT:.env | Affiche le fichier tel qu'il était dans le commit désigné. |
C'est ce qui rend Git utile : on peut revenir à n'importe quel état passé. Mais c'est aussi exactement ce qui empêche de « faire disparaître » un secret par une simple suppression.
Pourquoi supprimer ne suffit pas
Un secret commité se trouve, en réalité, à plusieurs endroits à la fois :
- L'historique local : tous les commits précédant la suppression conservent le fichier.
- Les copies déjà faites : chaque clone (un collègue, un serveur d'intégration, un autre poste) embarque l'historique complet, hors de portée de celui qui modifie l'original.
- Le dépôt distant et ses dérivés : le serveur (par exemple GitHub), les copies créées par d'autres comptes (forks) et les caches de moteurs de recherche ou d'outils d'analyse.
Plus un secret est resté longtemps accessible, plus la probabilité qu'il ait été copié augmente. Sur un dépôt public, il faut le considérer comme compromis dès l'instant où il a été envoyé.
Que faire quand un secret a été commité
L'ordre des mesures compte : la première est celle qui protège réellement.
| Ordre | Mesure | Pourquoi |
|---|---|---|
| 1 | Révoquer le secret (changer le mot de passe, régénérer la clé d'API) | Rend inutilisables toutes les copies, quel qu'en soit le nombre |
| 2 | Arrêter de le suivre : git rm --cached, .gitignore, secret rangé dans un fichier local | Évite que le nouveau secret fuie à son tour |
| 3 | Vérifier l'usage dans les journaux du service concerné | Savoir si quelqu'un s'en est déjà servi |
| 4 | Évaluer l'obligation légale si des données personnelles ont été exposées | Notification à la CNIL sous 72 heures, selon le risque (voir la séance sur les conséquences d'une fuite de données) |
| 5 | Nettoyer l'historique si nécessaire (git filter-repo) | Réduit l'exposition future, mais ne reprend jamais les copies déjà faites |
Réécrire l'historique est la mesure la moins efficace et la plus risquée : elle modifie les identifiants de tous les commits suivants, ce qui oblige chaque collègue à resynchroniser son dépôt. Elle ne remplace jamais la révocation du secret.
Si le secret vient d'être commité et que le dépôt n'a jamais été partagé, git commit --amend
(vu dans le cours Git) suffit à remplacer le dernier commit. Attention : l'ancien commit reste
récupérable un certain temps dans le journal local git reflog.
Éviter que cela se reproduise
- Écrire
.gitignoredès le premier commit, avant de créer le moindre fichier de secrets. - Versionner un modèle (
.env.example) qui liste les variables attendues avec des valeurs factices, pour que chacun sache quoi renseigner sans jamais recevoir de vrai secret via Git. - Relire avant de committer :
git status, puisgit diff --stagedpour voir ce qui part réellement, et évitergit add .quand le dossier contient des fichiers de configuration. - Automatiser la vigilance : des services comme GitHub proposent une détection automatique des
secrets (secret scanning, push protection), et des outils comme
gitleaksanalysent un dépôt, historique compris.
| Information | Peut-elle être dans le dépôt ? |
|---|---|
Code source, modèle .env.example | Oui |
| Nom du serveur, numéro de port (configuration non secrète) | Oui, avec précaution |
| Mot de passe, clé d'API, jeton d'accès, clé privée | Non : fichier local ignoré par Git |
Auditer un dépôt à la recherche de secrets oubliés (historique compris) fait l'objet d'un atelier dans une séance à venir.