Aller au contenu principal

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 :

  1. définir la variable ECOLE avec ECOLE="BTS SIO", puis afficher sa valeur depuis un shell enfant avec bash -c 'echo "ECOLE = $ECOLE"', avant et après export ECOLE
  2. exécuter ls -l /etc/sauvegarde.env et relever les droits ainsi que le propriétaire du fichier
  3. exécuter history | grep DB_PASSWORD et 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

info

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.

attention

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 :

  1. Affichez l'historique avec git log --oneline et relevez le nombre de commits.
  2. 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 ?
  3. 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 :

  1. Affichez la liste des fichiers du dossier avec ls -a. Le fichier .env est-il encore là ?
  2. Affichez l'historique avec git log --oneline et relevez le nombre de commits.
  3. Affichez les modifications de chaque commit qui a touché ce fichier avec git log -p -- .env. Retrouvez-vous le mot de passe ?
  4. 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 avec git 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 :

  1. Affichez la liste des fichiers du clone avec ls -a. Le fichier .env est-il présent ?
  2. Retrouvez le mot de passe dans l'historique du clone avec git log -p -- .env.
  3. 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 :

  1. Quelle est la toute première mesure à prendre face à ce mot de passe, avant même de toucher à Git ?
  2. Recréez un fichier .env local avec un nouveau mot de passe fort (en passant par nano pour qu'il n'apparaisse pas dans l'historique de votre session), puis créez un fichier .gitignore qui contient la ligne .env. Vérifiez avec git status que .env n'apparaît pas, puis avec git check-ignore -v .env que Git l'ignore bien.
  3. Ajoutez au dépôt un fichier .env.example qui liste les 3 variables avec une valeur vide pour DB_PASSWORD, puis committez .gitignore et .env.example en les nommant (sans git add .). Vérifiez avec git ls-files ce que Git suit désormais.
  4. 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 :
    1. rappeler pourquoi le mot de passe reste lisible après git rm et après un clone (historique conservé, copies hors de portée) ;
    2. présenter les mesures de l'étape 4 dans l'ordre et justifier cet ordre ;
    3. répondre aux questions de la classe.
astuce

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.

CommandeCe qui se passe réellement
git rm .env puis git commitUn nouveau commit est ajouté, dans lequel .env n'existe plus. Les commits précédents contiennent toujours .env.
git log -p -- .envAffiche chaque commit qui a touché ce fichier, avec les lignes ajoutées (+) et retirées (-).
git show IDENTIFIANT:.envAffiche le fichier tel qu'il était dans le commit désigné.
info

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.
attention

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.

OrdreMesurePourquoi
1Ré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
2Arrêter de le suivre : git rm --cached, .gitignore, secret rangé dans un fichier localÉvite que le nouveau secret fuie à son tour
3Vé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éesNotification à la CNIL sous 72 heures, selon le risque (voir la séance sur les conséquences d'une fuite de données)
5Nettoyer l'historique si nécessaire (git filter-repo)Réduit l'exposition future, mais ne reprend jamais les copies déjà faites
attention

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.

info

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 .gitignore dè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, puis git diff --staged pour voir ce qui part réellement, et éviter git 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 gitleaks analysent un dépôt, historique compris.
InformationPeut-elle être dans le dépôt ?
Code source, modèle .env.exampleOui
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éeNon : fichier local ignoré par Git
astuce

Auditer un dépôt à la recherche de secrets oubliés (historique compris) fait l'objet d'un atelier dans une séance à venir.


Test de mémorisation/compréhension​


Après un `git rm .env` suivi d'un commit, que devient le mot de passe qui figurait dans ce fichier ?


Quelle commande affiche, même pour un fichier supprimé, les modifications de chaque commit qui l'a touché ?


D'après le TP (étape 3), que prouve le fait que le clone du collègue contienne encore le mot de passe ?


Quand un mot de passe a été commité puis partagé, quelle est la toute première mesure à prendre ?


D'après le TP (étape 4), que fait la ligne `.env` écrite dans le fichier `.gitignore` ?


À quoi sert un fichier `.env.example` versionné dans le dépôt ?


Pourquoi `git add .` est-il risqué quand le dossier contient un fichier de secrets ?


Un mot de passe commité dans un dépôt public donnait accès à une base de clients, et les journaux montrent qu'un inconnu s'en est servi. Quelle obligation s'ajoute au changement du mot de passe ?


Pourquoi réécrire l'historique (par exemple avec `git filter-repo`) ne suffit-il pas à régler le problème ?