<link rel="stylesheet" href="/assets/fonts/jetbrains-mono/jetbrains-mono.css" />
All posts

Des mots de passe dans l'historique git : comment je les ai supprimés (et pourquoi ça ne suffit pas)

Je faisais une revue générale de mon projet, à la recherche de choses oubliées. J'en ai trouvé une dont je me serais bien passé : un fichier de configuration de déploiement contenant la clé qui signe les jetons de connexion, le mot de passe administrateur et le mot de passe d'envoi des e-mails. En clair, dans l'historique git, sur trois branches.

Comment c'est arrivé

Le fichier figurait dans le .gitignore. Le problème, c'est qu'il y a été ajouté après avoir été commité une première fois, et .gitignore n'ignore que les fichiers qui ne sont pas encore suivis : un fichier déjà suivi le reste, modification après modification, même si son nom est dans la liste.

Le dépôt était privé, mais privé ne veut pas dire sûr : les collaborateurs, les services de CI, les sauvegardes et chaque ordinateur sur lequel il a été cloné y ont accès.

Le supprimer de tout l'historique

Supprimer le fichier avec un nouveau commit ne sert à rien : il reste dans tous les commits précédents. Il faut réécrire l'historique de chaque branche. J'ai utilisé git filter-repo sur un clone miroir, puis j'ai forcé le push et vérifié qu'aucun commit ne le contenait plus :

# 1. Stop tracking the file (it stays on disk)
git rm --cached .env.deploy
git commit -m "Stop tracking .env.deploy"

# 2. Rewrite the history of EVERY branch on a mirror clone
git clone --mirror git@github.com:me/my-repo.git
cd my-repo.git
git filter-repo --path .env.deploy --invert-paths
git push --force --mirror

# 3. Verify: no commit on any branch may still contain it
git log --all --oneline -- .env.deploy

Après une telle réécriture, toute personne ayant une copie locale doit la réaligner sur le nouveau dépôt distant : les anciens commits ne doivent plus jamais être poussés.

Pourquoi ça ne suffit pas

C'est la leçon la plus importante. Même après la réécriture, les anciens commits peuvent rester accessibles : GitHub, par exemple, conserve des références internes aux pull requests qu'un push forcé ne peut pas effacer. Et on ne sait pas qui a téléchargé le dépôt par le passé.

Retirer un secret de l'historique évite d'aggraver la situation ; la seule chose qui la règle, c'est de le changer. Concrètement :

  • Une nouvelle clé de signature des jetons : elle invalide toutes les sessions existantes, donc tout le monde devra se reconnecter. C'est le prix à payer.
  • Un nouveau mot de passe administrateur.
  • La révocation du mot de passe d'application de messagerie et la création d'un nouveau.
  • La mise à jour des variables dans chaque environnement où elles sont utilisées.

La deuxième fois, le même schéma

Quelques semaines plus tard, j'ai retrouvé le même problème sous une autre forme : un script d'installation du serveur, très pratique, qui écrivait les identifiants directement dans le fichier de configuration. C'est le schéma récurrent : le script de confort qui contient les secrets pour ne rien avoir à configurer. Je l'ai réécrit pour qu'il génère les clés sur le moment et demande les mots de passe à la personne qui l'exécute :

# Before: secrets written into a script tracked by git
# JWT_SECRET=hardcoded-value...

# After: generated or asked at runtime, never committed
JWT_SECRET="$(openssl rand -base64 48)"
read -rsp "SMTP password: " SMTP_PASS; echo
umask 077   # the .env file is readable by its owner only
printf 'JWT_SECRET=%s\nSMTP_PASS=%s\n' "$JWT_SECRET" "$SMTP_PASS" > .env

Les habitudes qui évitent que cela se reproduise

  1. Un .env.example versionné, avec les noms des variables et aucune valeur ; le vrai .env n'entre jamais dans le dépôt.
  2. Un scanner de secrets avant chaque commit, comme gitleaks dans un hook pre-commit : il bloque le commit avant que le mal soit fait.
  3. La protection des push proposée par GitHub et GitLab, qui refuse les push contenant des secrets reconnaissables.
  4. Jamais de secrets dans les scripts : les générer à l'exécution ou les lire dans l'environnement.
  5. Une vérification périodique de l'historique : un secret que vous trouvez vous-même est un petit incident ; un secret trouvé par d'autres, non.

En résumé

Un secret dans un dépôt doit être considéré comme compromis dès l'instant où vous le trouvez. Réécrire l'historique est nécessaire mais pas suffisant : la vraie correction consiste à changer chaque identifiant exposé. Ensuite, il vaut mieux rendre le problème impossible à répéter, avec des scanners et des règles claires. J'ai rassemblé d'autres pratiques de sécurité dans ce guide.

💬 Notes des lecteurs

0 notes

Écrire une note

Partagez votre avis, une suggestion ou un compliment

Dernières notes

Aucune note pour le moment. Soyez le premier à commenter !