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 »Le hub de gestion des secrets situe ce guide parmi les autres : secrets statiques, cycle de vie, identité machine et coffres-forts.
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àPar défaut, Git conserve les entrées du reflog 90 jours, et 30 jours pour
celles qui pointent vers des commits devenus inaccessibles, comme le commit
retiré par un rebase (gc.reflogExpire et gc.reflogExpireUnreachable). Sur
les machines des autres développeurs, l'ancien historique 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 compromisPersonne n'avait révoqué le secret : il avait seulement disparu du code
courant. Un scan des fichiers présents, gitleaks dir, ne voit plus rien ; un
scan de l'historique, gitleaks git, le retrouve dans le commit d'origine.
C'est exactement ce que fait l'attaquant.
Scé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.
Le schéma suit le trajet d'un commit, du poste à la branche principale : chaque barrière rattrape ce que la précédente a laissé passer, et la seule que l'on ne contourne pas depuis le poste est le scan CI bloquant.
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.30.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 »Deux pièges attendent la personne qui écrit son premier .gitleaks.toml, et
tous deux ont été mesurés sur Gitleaks 8.22.1 et 8.30.1. Les chemins de
l'allowlist sont des expressions régulières, pas des motifs de fichiers :
un motif comme **/*_test.go fait planter Gitleaks au chargement
(panic: regexp: Compile), et avec lui le hook et la CI. Surtout, un fichier qui
déclare ses propres [[rules]] remplace les règles par défaut, sauf s'il
porte [extend] useDefault = true : sans cette ligne, le jeton GitHub du dépôt
de test n'était plus détecté, seule la règle maison restait active.
[extend]useDefault = true # garder les règles par défaut en plus des vôtres
[allowlist]paths = [ '''test/fixtures/''', # expressions régulières, pas des globs '''_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 complet (tous les commits)gitleaks git --verbose
# Scanner seulement les fichiers présents sur le disquegitleaks dir --verboseLa différence entre les deux commandes est au cœur de ce guide. Sur un dépôt
dont le secret a été retiré du code mais reste dans un commit antérieur,
gitleaks dir ne trouve rien et gitleaks git le trouve : le secret
« corrigé » n'a jamais quitté l'historique. Depuis la version 8.19, ces deux
sous-commandes remplacent gitleaks detect, qui fonctionne encore mais n'est
plus listée dans l'aide.
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
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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 persist-credentials: false
- uses: gitleaks/gitleaks-action@e0c47f4f8be36e29cdc102c57e68cb5cbf0e8d1e # v3.0.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}gitleaks: stage: security image: ghcr.io/gitleaks/gitleaks:v8.30.1@sha256:c00b6bd0aeb3071cbcb79009cb16a60dd9e0a7c60e2be9ab65d25e6bc8abbb7f script: - gitleaks git --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
Trois outils tiennent ce rôle : Gitleaks, l'implémentation de référence de la formation, TruffleHog, qui vérifie si un secret trouvé est encore actif, et detect-secrets, qui travaille avec une baseline des faux positifs acceptés.
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.txtgit-filter-repo retire le remote origin après la réécriture, pour
empêcher un push accidentel : mesuré, git remote est vide après l'opération.
Il faut donc le rajouter avant de pousser l'historique réécrit.
# Rajouter le remote retiré par git-filter-repogit remote add origin <url-du-depot>
# 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 | Non, chacun doit recloner |
| Forks | Non, hors de votre contrôle |
| Artefacts CI/CD | Non, suppression manuelle |
| Caches de build | Non, purge manuelle |
| Backups de la forge | Non, dépend de la plateforme |
| Systèmes d'analyse tiers | Non, 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 »Les problèmes ci-dessous reviennent au moment de mettre en place les
contrôles ou de nettoyer un dépôt. Trois d'entre eux ont été reproduits
en conteneur : la configuration Gitleaks qui plante, celle qui éteint les
règles par défaut, et le refus de git-filter-repo.
| Symptôme | Cause probable | Solution |
|---|---|---|
| Gitleaks détecte des faux positifs | Pattern trop large ou fichier de test | Ajouter le chemin, en expression régulière, dans [allowlist] du .gitleaks.toml |
git-filter-repo refuse : « does not look like a fresh clone » | Le dépôt n'est pas un clone frais : historique dans le reflog, ou fichiers modifiés | Travailler sur un clone frais et propre (git clone --no-local), ou forcer avec --force en connaissance de cause |
Gitleaks plante au démarrage : panic: regexp: Compile | Un chemin de l'allowlist écrit comme un glob (**/*_test.go) | Écrire les chemins en expressions régulières (_test\.go$) |
| Plus aucune alerte après l'ajout d'une règle maison | Des [[rules]] sans [extend] remplacent les règles par défaut | Ajouter [extend] useDefault = true au .gitleaks.toml |
| 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
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Score par domaine
Un domaine regroupe plusieurs guides et reçoit plusieurs questions : son score est plus fiable qu'un score par thème, qui porte souvent sur une seule question.
Bilan
Points forts
Aucun sujet réussi à 80 % sur au moins deux questions pour l’instant.
Points faibles
Aucun sujet sous le seuil de réussite.
À faire ensuite
Des indices pour retenter votre chance ?
Continuer le cours : Détecter les secrets exposés dans votre code
Continuer le cours : Secrets Scanning : détecter les secrets exposés dans le code
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications