Aller au contenu
Sécurité medium

Secrets dans le code et l'historique Git

18 min de lecture

Un développeur commit “temporairement” un mot de passe pour tester. Il le retire dans le commit suivant. Problème : l’historique Git conserve tout. Trois ans plus tard, un attaquant scanne les dépôts publics et trouve ce secret toujours valide.

Mais Git n’est que le point de départ. Une fois poussé, le secret se réplique dans les clones, les forks, les runners CI, les artefacts, les sauvegardes et les systèmes d’analyse. Ce guide couvre les trois couches nécessaires : prévenir, détecter et réagir.

  • Pourquoi supprimer un commit ne suffit pas — et ce qui est vraiment irrémédiable
  • Comment l’écosystème autour de Git multiplie la surface de fuite
  • Les outils de détection (Gitleaks, TruffleHog) et la push protection
  • Les mesures de prévention (hooks, CI, forge)
  • Comment réagir et nettoyer l’historique sans se raconter d’histoires

Quand vous commitez un secret puis le supprimez :

Fenêtre de terminal
# Commit 1 : ajout du secret
git add .env
git commit -m "Add config"
# Commit 2 : suppression du secret
git rm .env
git commit -m "Remove config"

Le fichier n’apparaît plus dans HEAD, mais il existe toujours dans l’historique :

Fenêtre de terminal
# Le secret est toujours accessible
git show HEAD~1:.env
# DB_PASSWORD=SuperSecret123
Fenêtre de terminal
# Tentative de réécriture d'historique
git rebase -i HEAD~2
# Suppression du commit contenant le secret
git push --force

Un force push réécrit l’historique du dépôt distant, mais il n’efface pas le secret partout :

  • Les clones existants conservent l’ancien historique
  • Les forks conservent l’ancien historique
  • Les systèmes de backup conservent l’ancien historique
  • Les caches CI conservent potentiellement l’historique

Même après un rebase, le reflog local garde une trace :

Fenêtre de terminal
git reflog
# abc1234 HEAD@{0}: rebase: ...
# def5678 HEAD@{1}: commit: Add config # ← Le secret est encore là

Le reflog expire après 90 jours par défaut, mais sur les machines des autres développeurs, il reste intact.

Un secret committé ne disparaît pas avec un simple git rm. L’historique Git est distribué : chaque clone, chaque fork conserve une copie complète. Mais le problème va bien au-delà du dépôt lui-même.

Une fois le secret poussé, il peut être répliqué dans :

  • les clones de développeurs (chacun a une copie complète)
  • les forks (y compris ceux d’ex-collaborateurs)
  • les runners CI/CD (qui clonent le dépôt à chaque pipeline)
  • les caches de build (Docker layers, cache npm, etc.)
  • les artefacts de pipeline (logs, rapports, archives)
  • les sauvegardes de la forge (GitHub, GitLab, Gitea)
  • les mirrors et répliques automatiques
  • les systèmes d’analyse branchés au dépôt (SonarQube, Snyk, etc.)
  • l’indexation temporaire par la forge (recherche GitHub, API events)
SurfacePersistanceNettoyable ?
Historique Git (dépôt d’origine)PermanenteOui, avec git-filter-repo
Clones développeursJusqu’au recloneNécessite coordination
ForksPermanenteNon — hors de votre contrôle
Runners CI/CDDurée du job (ou cache)Purge manuelle
Artefacts de pipelineSelon la rétentionSuppression manuelle
Mirrors / backups forgeVariableDépend de la plateforme
Indexation temporaireQuelques heuresAutomatique
Systèmes d’analyse tiersVariableDépend du service

La colonne Nettoyable ? distingue ce qui est irrémédiable (forks, clones externes) de ce que vous pouvez encore maîtriser.

Il faut distinguer deux types d’acteurs :

Détection défensive (pour vous protéger) :

  • GitHub Secret Scanning : détecte les patterns connus après le commit et alerte le mainteneur
  • GitHub Push Protection : bloque le push avant qu’il n’entre dans le dépôt (voir section dédiée)
  • Chercheurs en sécurité : scans avec TruffleHog ou Gitleaks

Exploitation opportuniste (pour vous attaquer) :

  • Bots malveillants : scannent les événements publics GitHub en temps réel et exploitent les clés trouvées automatiquement

Sur un dépôt public, certaines clés peuvent être détectées et exploitées très rapidement, parfois en quelques minutes.

Certains patterns de secrets sont détectés automatiquement par les scanners. Les bots malveillants ciblent en priorité les clés cloud car elles donnent un accès immédiat à des ressources payantes.

TypePatternRisque
AWS Access KeyAKIA... (20 caractères)Accès cloud complet
GitHub PATghp_...Accès aux dépôts
Stripe API Keysk_live_...Transactions financières
JWT SecretVariableForge de tokens
Private Key-----BEGIN RSA PRIVATE KEY-----Usurpation d’identité
Database URLpostgres://user:pass@hostAccès base de données
.npmrc / .pypirc_authToken=...Publication de paquets
docker/config.json"auth": "base64..."Push vers un registry
kubeconfigtoken: ou certificat clientAccès cluster Kubernetes
Terraform Cloud token*.atlasv1.*Gestion d’infrastructure
Certificat client-----BEGIN CERTIFICATE-----Authentification mTLS

Scénario 1 : la clé AWS exploitée en quelques minutes

Section intitulée « Scénario 1 : la clé AWS exploitée en quelques minutes »
T+0min : Développeur push un commit avec clé AWS
T+2min : Bot scanne le dépôt public via l'API GitHub
T+3min : Bot extrait la clé et lance des instances EC2 pour miner
T+5min : Facture AWS qui explose
T+1h : Développeur remarque le problème

Coût réel : des milliers d’euros de facturation cloud.

T-3ans : Secret committé pour un test
T-3ans : Secret supprimé dans le commit suivant
T : Attaquant scanne l'historique
T+1h : Secret toujours valide, accès prod compromis
T : Dépôt privé avec secret dans l'historique
T : Employé quitte l'entreprise, garde son fork
T+6m : L'ex-employé a toujours accès au secret

Même après nettoyage de l’historique sur le dépôt d’origine, le fork conserve sa propre copie complète. C’est pourquoi la révocation du secret est la seule action fiable.

La prévention repose sur plusieurs couches complémentaires. Aucune n’est suffisante à elle seule.

Les hooks locaux bloquent le commit si un secret est détecté :

.pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.22.1
hooks:
- id: gitleaks
Fenêtre de terminal
# Installation
pip install pre-commit
pre-commit install
# Test
git commit -m "Test"
# Gitleaks scanne automatiquement les fichiers modifiés
.gitleaks.toml
[allowlist]
paths = [
"test/fixtures/*",
"**/*_test.go",
]
[[rules]]
id = "custom-api-key"
description = "Custom API Key"
regex = '''custom_api_key\s*=\s*['"]([^'"]+)['"]'''

GitHub propose une push protection qui scanne le contenu au moment du push et bloque l’envoi si un secret connu est détecté. C’est une brique de prévention très forte, différente du secret scanning a posteriori :

MécanismeQuandAction
Push protectionAu moment du pushBloque l’envoi
Secret scanningAprès le commitAlerte le mainteneur

Avantages de la push protection :

  • Agit avant que le secret n’entre dans le dépôt
  • Couvre les patterns connus (clés cloud, tokens de forges, etc.)
  • Complémentaire des hooks locaux (protège même sans pre-commit)

Limites :

  • Ne détecte que les patterns connus de la forge
  • Un développeur peut forcer le passage (avec justification)
  • Ne remplace pas les scans CI sur l’historique complet

Pour les organisations, GitHub permet aussi de définir des custom patterns pour le secret scanning et la push protection, adaptés aux formats de secrets internes.

# Secrets et configurations locales
.env
.env.*
*.key
*.pem
*.p12
*.jks
secrets/
credentials/
# Registres de paquets
.npmrc
.pypirc
# Docker
docker/config.json
# Kubernetes
kubeconfig
*.kubeconfig
# IDE
.idea/
.vscode/settings.json
# Logs qui peuvent contenir des secrets
*.log
logs/

Gitleaks est un scanner rapide basé sur des règles (regex). Il est particulièrement adapté aux hooks pre-commit et à l’intégration CI.

Fenêtre de terminal
# Scanner l'historique complet
gitleaks detect --source . --verbose
# Sortie
Finding:
Description: AWS Access Key ID
File: config/database.yml
Commit: abc1234
Author: dev@company.com
Line: 15
Secret: AKIAIOSFODNN7EXAMPLE

Points forts :

  • Rapide — scanne des dépôts volumineux en secondes
  • Patterns préconfigurés — couvre les formats les plus courants
  • Facile à intégrer — pre-commit, GitHub Actions, GitLab CI

TruffleHog va plus loin avec la vérification active : il teste si le secret trouvé est encore valide en tentant de l’utiliser. Cela permet de prioriser les vrais secrets actifs et d’écarter les faux positifs ou les secrets déjà révoqués.

Fenêtre de terminal
# Scanner avec vérification active
trufflehog git file://. --only-verified
# Sortie
Found verified result
Detector Type: AWS
Raw result: AKIAIOSFODNN7EXAMPLE
Commit: abc1234
Email: dev@company.com

Points forts :

  • Vérification active — distingue un secret exploitable d’un faux positif ou d’un secret déjà révoqué
  • Détection par entropie — repère les secrets qui ne suivent pas un pattern connu
  • Classification — catégorise les résultats par type et par risque

Le scan en CI est la dernière barrière avant que le code n’atteigne la branche principale. Il doit bloquer la PR si un secret est détecté.

name: Secret Scan
on: [push, pull_request]
permissions: {}
jobs:
gitleaks:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
persist-credentials: false
- uses: gitleaks/gitleaks-action@ff98106e4c7b2bc287a672a9df4d3a2465ba702f # v2.3.9
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Bonnes pratiques CI :

  • Pinner les actions par SHA — éviter les tags mutables
  • Scanner le diff sur les PR — rapide, cible les changements
  • Scanner l’historique complet périodiquement — attrape les secrets anciens
  • Bloquer la PR en cas de détection — pas de allow_failure: true
  1. Révoquer immédiatement

    Changez le secret avant de nettoyer l’historique. Un attaquant peut avoir déjà copié le secret.

  2. Évaluer l’impact

    Depuis quand le secret était-il exposé ? Quels accès donne-t-il ? Le dépôt est-il public ou privé ?

  3. Rechercher une utilisation malveillante

    Vérifiez les logs d’audit du service concerné (CloudTrail pour AWS, audit logs pour GitHub, etc.).

  4. Nettoyer l’historique

    Voir la section suivante sur git-filter-repo.

  5. Purger les artefacts

    • Supprimer les artefacts CI/CD contenant le secret
    • Vider les caches de build (Docker, npm, etc.)
    • Demander aux collaborateurs de recloner le dépôt
  6. Prévenir la récidive

    Installer les pre-commit hooks, les scans CI, et activer la push protection.

Nettoyer l’historique : ce que cela fait, et ce que cela ne fait pas

Section intitulée « Nettoyer l’historique : ce que cela fait, et ce que cela ne fait pas »

git-filter-repo est l’outil recommandé pour réécrire l’historique (il remplace l’obsolète git filter-branch).

Fenêtre de terminal
# Supprimer un fichier de tout l'historique
git filter-repo --invert-paths --path config/secrets.yml
# Remplacer un pattern dans tout l'historique
git filter-repo --replace-text expressions.txt

Après le nettoyage :

Fenêtre de terminal
# Force push sur toutes les branches
git push --force --all
git push --force --tags

Réécrire l’historique du dépôt d’origine est nécessaire, mais ne suffit pas :

SurfaceNettoyée automatiquement ?
Dépôt d’origine✅ Oui (après force push)
Clones des développeurs❌ Chacun doit recloner
Forks❌ Hors de votre contrôle
Artefacts CI/CD❌ Suppression manuelle
Caches de build❌ Purge manuelle
Backups de la forge❌ Dépend de la plateforme
Systèmes d’analyse tiers❌ Dépend du service

Conséquence : le nettoyage d’historique est utile pour l’hygiène du dépôt, mais la révocation du secret reste la seule action vraiment fiable. Si un fork ou un clone externe a capturé le secret, aucune réécriture de votre côté ne l’effacera.

SymptômeCause probableSolution
Gitleaks détecte des faux positifsPattern trop large ou fichier de testAjouter le chemin dans [allowlist] du .gitleaks.toml
git-filter-repo refuse de s’exécuterDépôt avec des modifications non commitéesCommiter ou stasher les changements
Secret détecté mais déjà révoquéPas de filtrage par validitéUtiliser trufflehog --only-verified pour prioriser
Hook pre-commit contournéDéveloppeur a utilisé --no-verifyAjouter un scan CI bloquant comme filet de sécurité
Push protection contournéeDéveloppeur a forcé le passageVérifier les justifications dans les logs de la forge
Collaborateurs en erreur après force pushSHA des commits réécritsDemander à l’équipe de recloner le dépôt
  1. Dès qu’un secret atteint Git, il est compromis — la révocation est la seule action fiable
  2. Git n’est que le point de départ — le secret se propage dans les clones, forks, runners CI, artefacts et sauvegardes
  3. Révoquer avant de nettoyer — le nettoyage d’historique n’efface pas les copies externes
  4. Trois couches de prévention — pre-commit hooks + push protection + scan CI bloquant
  5. Aucune couche ne suffit seule — les hooks sont contournables, la push protection ne couvre que les patterns connus, la CI ne protège qu’après le push
  6. Le nettoyage d’historique a ses limitesgit-filter-repo réécrit le dépôt, mais pas les forks ni les caches externes

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn