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.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- 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
Prérequis
Section intitulée « Prérequis »Pourquoi supprimer un commit ne suffit pas
Section intitulée « Pourquoi supprimer un commit ne suffit pas »L’historique Git est immuable (par défaut)
Section intitulée « L’historique Git est immuable (par défaut) »Quand vous commitez un secret puis le supprimez :
# Commit 1 : ajout du secretgit add .envgit commit -m "Add config"
# Commit 2 : suppression du secretgit rm .envgit commit -m "Remove config"Le fichier n’apparaît plus dans HEAD, mais il existe toujours dans
l’historique :
# Le secret est toujours accessiblegit show HEAD~1:.env# DB_PASSWORD=SuperSecret123Même après un rebase ou un force push
Section intitulée « Même après un rebase ou un force push »# Tentative de réécriture d'historiquegit rebase -i HEAD~2# Suppression du commit contenant le secretgit push --forceUn 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
Le reflog conserve tout (localement)
Section intitulée « Le reflog conserve tout (localement) »Même après un rebase, le reflog local garde une trace :
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.
Git n’est que le point de départ de la fuite
Section intitulée « Git n’est que le point de départ de la fuite »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)
| Surface | Persistance | Nettoyable ? |
|---|---|---|
| Historique Git (dépôt d’origine) | Permanente | Oui, avec git-filter-repo |
| Clones développeurs | Jusqu’au reclone | Nécessite coordination |
| Forks | Permanente | Non — hors de votre contrôle |
| Runners CI/CD | Durée du job (ou cache) | Purge manuelle |
| Artefacts de pipeline | Selon la rétention | Suppression manuelle |
| Mirrors / backups forge | Variable | Dépend de la plateforme |
| Indexation temporaire | Quelques heures | Automatique |
| Systèmes d’analyse tiers | Variable | Dépend du service |
La colonne Nettoyable ? distingue ce qui est irrémédiable (forks, clones externes) de ce que vous pouvez encore maîtriser.
Qui scanne les dépôts publics ?
Section intitulée « Qui scanne les dépôts publics ? »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.
Types de secrets fréquemment exposés
Section intitulée « Types de secrets fréquemment exposés »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.
| Type | Pattern | Risque |
|---|---|---|
| AWS Access Key | AKIA... (20 caractères) | Accès cloud complet |
| GitHub PAT | ghp_... | Accès aux dépôts |
| Stripe API Key | sk_live_... | Transactions financières |
| JWT Secret | Variable | Forge de tokens |
| Private Key | -----BEGIN RSA PRIVATE KEY----- | Usurpation d’identité |
| Database URL | postgres://user:pass@host | Accès base de données |
.npmrc / .pypirc | _authToken=... | Publication de paquets |
docker/config.json | "auth": "base64..." | Push vers un registry |
| kubeconfig | token: ou certificat client | Accès cluster Kubernetes |
| Terraform Cloud token | *.atlasv1.* | Gestion d’infrastructure |
| Certificat client | -----BEGIN CERTIFICATE----- | Authentification mTLS |
Scénarios de compromission
Section intitulée « Scénarios de compromission »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é AWST+2min : Bot scanne le dépôt public via l'API GitHubT+3min : Bot extrait la clé et lance des instances EC2 pour minerT+5min : Facture AWS qui exploseT+1h : Développeur remarque le problèmeCoût réel : des milliers d’euros de facturation cloud.
Scénario 2 : le secret “temporaire” oublié
Section intitulée « Scénario 2 : le secret “temporaire” oublié »T-3ans : Secret committé pour un testT-3ans : Secret supprimé dans le commit suivantT : Attaquant scanne l'historiqueT+1h : Secret toujours valide, accès prod compromisScénario 3 : le fork de l’ex-employé
Section intitulée « Scénario 3 : le fork de l’ex-employé »T : Dépôt privé avec secret dans l'historiqueT : Employé quitte l'entreprise, garde son forkT+6m : L'ex-employé a toujours accès au secretMê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.
Prévenir les fuites
Section intitulée « Prévenir les fuites »La prévention repose sur plusieurs couches complémentaires. Aucune n’est suffisante à elle seule.
Pre-commit hooks (première barrière)
Section intitulée « Pre-commit hooks (première barrière) »Les hooks locaux bloquent le commit si un secret est détecté :
repos: - repo: https://github.com/gitleaks/gitleaks rev: v8.22.1 hooks: - id: gitleaks# Installationpip install pre-commitpre-commit install
# Testgit commit -m "Test"# Gitleaks scanne automatiquement les fichiers modifiésConfiguration personnalisée de Gitleaks
Section intitulée « Configuration personnalisée de Gitleaks »[allowlist]paths = [ "test/fixtures/*", "**/*_test.go",]
[[rules]]id = "custom-api-key"description = "Custom API Key"regex = '''custom_api_key\s*=\s*['"]([^'"]+)['"]'''Push protection de la forge
Section intitulée « Push protection de la forge »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écanisme | Quand | Action |
|---|---|---|
| Push protection | Au moment du push | Bloque l’envoi |
| Secret scanning | Après le commit | Alerte 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.
.gitignore : réduire les erreurs courantes
Section intitulée « .gitignore : réduire les erreurs courantes »# Secrets et configurations locales.env.env.**.key*.pem*.p12*.jkssecrets/credentials/
# Registres de paquets.npmrc.pypirc
# Dockerdocker/config.json
# Kuberneteskubeconfig*.kubeconfig
# IDE.idea/.vscode/settings.json
# Logs qui peuvent contenir des secrets*.loglogs/Détecter les secrets
Section intitulée « Détecter les secrets »Gitleaks
Section intitulée « Gitleaks »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.
# Scanner l'historique completgitleaks detect --source . --verbose
# SortieFinding: Description: AWS Access Key ID File: config/database.yml Commit: abc1234 Author: dev@company.com Line: 15 Secret: AKIAIOSFODNN7EXAMPLEPoints 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
Section intitulée « TruffleHog »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.
# Scanner avec vérification activetrufflehog git file://. --only-verified
# SortieFound verified resultDetector Type: AWSRaw result: AKIAIOSFODNN7EXAMPLECommit: abc1234Email: dev@company.comPoints 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
Scan CI bloquant
Section intitulée « Scan CI bloquant »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 Scanon: [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 }}gitleaks: stage: security image: ghcr.io/gitleaks/gitleaks:v8.22.1 script: - gitleaks detect --source . --verbose --redact allow_failure: falseBonnes 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
Réagir à une fuite de secret
Section intitulée « Réagir à une fuite de secret »-
Révoquer immédiatement
Changez le secret avant de nettoyer l’historique. Un attaquant peut avoir déjà copié le secret.
-
É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é ?
-
Rechercher une utilisation malveillante
Vérifiez les logs d’audit du service concerné (CloudTrail pour AWS, audit logs pour GitHub, etc.).
-
Nettoyer l’historique
Voir la section suivante sur
git-filter-repo. -
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
-
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).
Ce que git-filter-repo fait
Section intitulée « Ce que git-filter-repo fait »# Supprimer un fichier de tout l'historiquegit filter-repo --invert-paths --path config/secrets.yml
# Remplacer un pattern dans tout l'historiquegit filter-repo --replace-text expressions.txtAprès le nettoyage :
# Force push sur toutes les branchesgit push --force --allgit push --force --tagsCe que le nettoyage ne fait PAS
Section intitulée « Ce que le nettoyage ne fait PAS »Réécrire l’historique du dépôt d’origine est nécessaire, mais ne suffit pas :
| Surface | Nettoyé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.
Dépannage
Section intitulée « Dépannage »| Symptôme | Cause probable | Solution |
|---|---|---|
| Gitleaks détecte des faux positifs | Pattern trop large ou fichier de test | Ajouter le chemin dans [allowlist] du .gitleaks.toml |
git-filter-repo refuse de s’exécuter | Dépôt avec des modifications non commitées | Commiter 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-verify | Ajouter un scan CI bloquant comme filet de sécurité |
| Push protection contournée | Développeur a forcé le passage | Vérifier les justifications dans les logs de la forge |
| Collaborateurs en erreur après force push | SHA des commits réécrits | Demander à l’équipe de recloner le dépôt |
À retenir
Section intitulée « À retenir »- Dès qu’un secret atteint Git, il est compromis — la révocation est la seule action fiable
- Git n’est que le point de départ — le secret se propage dans les clones, forks, runners CI, artefacts et sauvegardes
- Révoquer avant de nettoyer — le nettoyage d’historique n’efface pas les copies externes
- Trois couches de prévention — pre-commit hooks + push protection + scan CI bloquant
- 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
- Le nettoyage d’historique a ses limites —
git-filter-reporéécrit le dépôt, mais pas les forks ni les caches externes