gitleaks, git-secrets und die richtige Reaktion im Ernstfall
Ein vergessener API-Key in einem Commit reicht aus, um Zugangsdaten dauerhaft offenzulegen, selbst wenn die Datei später wieder gelöscht wird. Dieser Artikel zeigt, wie gitleaks und git-secrets Secrets schon vor dem Commit erkennen, warum eine zusätzliche CI-Prüfung nötig ist, und welche Reihenfolge bei einem tatsächlichen Leak über den Schaden entscheidet.
Inhaltsverzeichnis
- 1. Wie Secrets trotz guter Absicht in die Git-Historie gelangen
- 2. Warum das Löschen der Datei in einem späteren Commit das Secret nicht entfernt
- 3. gitleaks: Installation und Nutzung als Pre-Commit-Hook
- 4. .gitleaks.toml: eigene Regeln, Entropie- und Regex-Erkennung
- 5. git-secrets: AWS-fokussierter Scanner mit Git-Hook-Integration
- 6. Das pre-commit-Framework: zentrale Verwaltung von Hooks
- 7. CI-basiertes Scanning als zweite Verteidigungslinie
- 8. Ein Secret ist geleakt: Rotation zuerst, Historie bereinigen danach
- 9. False Positives, Secret Manager und organisatorische Prävention
- 10. Zusammenfassung
- 11. FAQ
1. Wie Secrets trotz guter Absicht in die Git-Historie gelangen
Kaum ein Entwickler committet absichtlich ein Passwort. Trotzdem landen Secrets ständig in Git-Repositories, meist über drei immer wiederkehrende Wege. Der erste ist der klassische vergessene .env-Commit: Eine lokale .env-Datei mit echten Datenbank-Zugangsdaten liegt versehentlich nicht in der .gitignore, ein schneller git add . erfasst sie mit, und der Commit ist raus, bevor jemand es bemerkt. Der zweite Weg ist der hardcodierte API-Key in einer Konfigurationsdatei, oft als schnelle Zwischenlösung während der Entwicklung gedacht, die nie wieder entfernt wurde, weil die Datei danach als funktionierend galt und niemand sie erneut angefasst hat.
Der dritte und unterschätzte Weg ist der Debug-Commit: Zur Fehlersuche wird kurzzeitig ein var_dump($apiKey) oder ein eingeloggtes Zugangstoken in eine Log-Ausgabe eingefügt, versehentlich mitcommittet und im nächsten Commit "korrigiert". Genau dieser Fall ist tückisch, denn der Entwickler denkt, das Problem sei behoben, sobald die Zeile wieder entfernt ist. Tatsächlich bleibt das Secret in der Historie erhalten und ist für jeden späteren Klon des Repositories weiterhin abrufbar.
2. Warum das Löschen der Datei in einem späteren Commit das Secret nicht entfernt
Git speichert jeden Commit als vollständigen, unveränderlichen Snapshot. Ein neuer Commit, der eine Datei löscht oder ein Secret durch einen Platzhalter ersetzt, erzeugt lediglich einen weiteren Snapshot, in dem das Secret nicht mehr vorkommt. Der alte Snapshot mit dem echten Wert bleibt als eigenständiges Blob-Objekt unter .git/objects bestehen und ist über git log -p, git show <commit> oder einfach durch das Auschecken eines älteren Commits weiterhin vollständig sichtbar. Für Git ist Löschen kein Überschreiben, sondern nur ein weiterer Zustand in einer Kette, die alle vorherigen Zustände bewahrt.
Das bedeutet konkret: Sobald ein Commit mit einem Secret einmal gepusht wurde, reicht ein einzelner git fetch irgendeines Mitarbeiters, Forks, CI-Runners oder automatisierten Mirrors, um das Secret dauerhaft zu vervielfältigen. Eine echte Bereinigung erfordert eine Umschreibung der Historie mit Tools wie git filter-repo oder BFG Repo-Cleaner, gefolgt von einem Force-Push und einem verpflichtenden Neu-Klonen für jeden Entwickler. Das ist aufwendig, störend für das gesamte Team, und löst trotzdem nicht das eigentliche Problem, dass der Wert bereits kompromittiert ist.
3. gitleaks: Installation und Nutzung als Pre-Commit-Hook
gitleaks ist ein in Go geschriebener, sehr schneller Secret-Scanner, der sowohl den kompletten Verlauf eines Repositories als auch nur den aktuell gestagten Inhalt durchsuchen kann. Die Installation erfolgt über Homebrew, den vorkompilierten Release-Binary oder go install, und funktioniert ohne weitere Abhängigkeiten auf der Kommandozeile. Der Befehl gitleaks detect durchsucht die gesamte Historie eines Repositories und meldet jeden Treffer mit Commit-Hash, Datei und einer redigierten Vorschau des gefundenen Werts.
Für den täglichen Workflow ist gitleaks protect --staged entscheidend: Der Befehl prüft ausschließlich das, was gerade mit git add für den nächsten Commit vorgemerkt wurde, genau der Zeitpunkt, an dem ein Eingreifen noch ohne Historienbereinigung möglich ist. Als natives Git-Hook-Skript unter .git/hooks/pre-commit eingebunden, blockiert gitleaks jeden Commit, der ein erkanntes Secret enthält, bevor es überhaupt lokal committet wird, geschweige denn gepusht.
# Install gitleaks (macOS/Linux via Homebrew)
brew install gitleaks
# Or download the prebuilt binary directly
curl -sSL https://github.com/gitleaks/gitleaks/releases/latest/download/gitleaks_8.18.4_linux_x64.tar.gz | tar -xz gitleaks
sudo mv gitleaks /usr/local/bin/
# Scan the full commit history of the current repository
gitleaks detect --source . --verbose
# Scan only what is currently staged, before a commit is created
gitleaks protect --staged --verbose
# Install gitleaks as a native git pre-commit hook
cat > .git/hooks/pre-commit << 'EOF'
#!/usr/bin/env bash
set -euo pipefail
gitleaks protect --staged --redact --verbose
EOF
chmod +x .git/hooks/pre-commit
4. .gitleaks.toml: eigene Regeln, Entropie- und Regex-Erkennung
gitleaks bringt ein umfangreiches Set an Standardregeln mit, das bekannte Formate wie AWS-Zugriffsschlüssel, Stripe-Tokens oder private SSH-Schlüssel per regexbasierter Erkennung zuverlässig findet. Regex-Regeln sind präzise, weil sie ein bekanntes Präfix oder Format prüfen, etwa AKIA gefolgt von 16 Zeichen für einen AWS-Key. Sie versagen jedoch bei projektspezifischen Secrets ohne festes Format, etwa einem selbst generierten internen API-Token. Genau hier setzt die entropiebasierte Erkennung an: Sie bewertet die statistische Zufälligkeit eines Strings und markiert Werte, die zu "chaotisch" aussehen, um natürlicher Text oder Code zu sein.
Eigene Regeln, Entropie-Schwellenwerte und eine Allowlist für bekannte Ausnahmen werden in .gitleaks.toml im Projekt-Root gepflegt. Mit [extend] useDefault = true bleiben die Standardregeln aktiv, während projektspezifische Muster, etwa für Magento-Datenbankpasswörter in env.php, gezielt ergänzt werden. Die Allowlist verhindert, dass Testdaten, Bilddateien oder Vendor-Verzeichnisse fälschlicherweise als Fund gemeldet werden.
# .gitleaks.toml: custom rules, allowlist and severity tuning
title = "mironsoft gitleaks config"
[extend]
# Start from the built-in default ruleset, then extend it
useDefault = true
[[rules]]
id = "magento-db-password"
description = "Hardcoded Magento database password in env.php"
regex = '''(?i)'password'\s*=>\s*'[^']{8,}'''
tags = ["magento", "credentials"]
[[rules]]
id = "generic-high-entropy-token"
description = "Generic high-entropy string, catches unknown token formats"
regex = '''[A-Za-z0-9_\-]{32,}'''
entropy = 4.5
secretGroup = 0
[allowlist]
description = "Known false positives"
paths = [
'''(.*?)(png|jpg|jpeg|gif|svg|lock)$''',
'''vendor/.*''',
]
regexes = [
'''EXAMPLE_[A-Z_]+''',
]
5. git-secrets: AWS-fokussierter Scanner mit Git-Hook-Integration
git-secrets von AWS Labs verfolgt einen bewusst schlankeren Ansatz als gitleaks: Es arbeitet ausschließlich mit expliziten Regex-Mustern, ohne Entropie-Heuristik, und bringt vorgefertigte Muster speziell für AWS-Zugangsdaten mit. Nach der Installation über make install registriert git secrets --install die Prüfung direkt als Git-Hooks im lokalen Repository, während git secrets --register-aws die AWS-spezifischen Muster für Access Keys und Secret Keys aktiviert.
Eigene Muster lassen sich projektspezifisch über git secrets --add ergänzen, etwa für interne Token-Formate oder Magento Marketplace-Zugangsdaten. Bekannte, unkritische Treffer, zum Beispiel ein Dummy-Key in der README, werden mit git secrets --add --allowed gezielt ausgenommen. Der Befehl git secrets --scan-history prüft zusätzlich die komplette bestehende Historie, was besonders bei der Einführung von git-secrets in ein bereits gewachsenes Repository wichtig ist, um Altlasten aufzudecken.
# Install git-secrets (AWS Labs)
git clone https://github.com/awslabs/git-secrets.git
cd git-secrets && sudo make install
# Register AWS-specific patterns and install as git hooks in this repo
cd /path/to/repo
git secrets --install
git secrets --register-aws
# Add a project-specific regex pattern, e.g. a Magento authorization key
git secrets --add '[0-9a-f]{32}'
git secrets --add --literal 'MAGENTO_MARKETPLACE_TOKEN'
# Scan the entire history, not just the working tree
git secrets --scan-history
# Allow a known-safe match (e.g. a documented dummy key in README)
git secrets --add --allowed 'EXAMPLE_API_KEY_00000000000000000000000000000000'
6. Das pre-commit-Framework: zentrale Verwaltung von Hooks
Statt Hook-Skripte manuell pro Repository unter .git/hooks zu pflegen, was bei jedem Klon verloren geht, übernimmt das pre-commit-Framework die zentrale Verwaltung über eine versionierte .pre-commit-config.yaml. Installiert wird es einmalig pro Rechner über pip install pre-commit oder brew install pre-commit, aktiviert wird es pro Repository mit pre-commit install. Der entscheidende Vorteil: Die Konfiguration liegt im Repository selbst, ist damit Teil des Codes und wird für jeden Mitarbeiter automatisch mitgeliefert, sobald der Hook einmal installiert ist.
gitleaks bringt eine offizielle pre-commit-Hook-Definition mit, die sich mit wenigen Zeilen einbinden lässt und automatisch nur gestagte Änderungen prüft. Wichtig zu verstehen: Ein lokaler Hook ist trotzdem kein vollständiger Schutz. Er lässt sich mit git commit --no-verify bewusst umgehen, und auf einem frisch geklonten Repository greift er erst, nachdem pre-commit install explizit ausgeführt wurde. Genau diese Lücke macht eine zweite, serverseitige Prüfung unverzichtbar.
# Install the pre-commit framework once per machine
# pip install pre-commit
# brew install pre-commit
#
# Then activate it inside this repository:
# pre-commit install
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.4
hooks:
- id: gitleaks
- repo: local
hooks:
- id: block-env-files
name: Block committing real .env files
entry: sh -c '! git diff --cached --name-only | grep -E "^\.env$"'
language: system
stages: [commit]
7. CI-basiertes Scanning als zweite Verteidigungslinie
Pre-Commit-Hooks sind wertvoll, aber freiwillig: Sie lassen sich mit --no-verify umgehen, existieren nicht auf einem frisch geklonten Repository ohne pre-commit install, und schützen nicht vor Commits, die direkt über die GitHub-Weboberfläche oder ein CI-generiertes Skript entstehen. Ein CI-Job, der gitleaks bei jedem Push und jeder Pull-Request-Aktualisierung ausführt, schließt genau diese Lücke serverseitig, unabhängig davon, ob und wie ein einzelner Entwickler seine lokale Umgebung konfiguriert hat.
Entscheidend ist, dass der CI-Job die Pipeline mit einem Fehler-Exit-Code beendet, sobald gitleaks einen Fund meldet, und dass der Checkout mit vollständiger Historie (fetch-depth: 0) erfolgt, statt nur den letzten Commit zu prüfen. So wird ein Merge in den Hauptbranch blockiert, bevor ein Secret dort ankommt, selbst wenn der lokale Hook aus welchem Grund auch immer nicht gegriffen hat.
# .github/workflows/secrets-scan.yml
name: Secrets Scan
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
gitleaks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # full history, not just the last commit
- name: Run gitleaks
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Action exits non-zero on any finding, failing the pipeline
8. Ein Secret ist geleakt: Rotation zuerst, Historie bereinigen danach
Wird trotz aller Vorkehrungen ein echtes Secret entdeckt, entscheidet die Reihenfolge der nächsten Schritte über das tatsächliche Risiko. Der erste und nicht verhandelbare Schritt ist die sofortige Rotation der Zugangsdaten an der Quelle: der API-Key wird beim Anbieter widerrufen und durch einen neuen ersetzt, das Datenbankpasswort wird geändert, das Token wird invalidiert. Erst danach folgt die Bereinigung der Git-Historie mit git filter-repo oder BFG Repo-Cleaner.
Der Grund für diese Reihenfolge ist einfach: Sobald ein Commit gepusht wurde, war der Wert bereits für jeden mit Zugriff sichtbar, für Mitarbeiter, CI-Runner, Caches, Forks und lokale Klone. Eine Historienbereinigung ohne vorherige Rotation entfernt das Secret zwar aus dem Repository, ändert aber nichts daran, dass es bereits kompromittiert wurde und weiterhin gültig ist. CDN-Caches, unabhängige Forks und längst heruntergeladene Klone behalten die alte Historie ohnehin, ganz gleich, wie gründlich der Force-Push im Ursprungsrepository durchgeführt wird. Erst eine ungültig gemachte Zugangsdatei macht das Secret wertlos für jeden, der es bereits gesehen hat.
9. False Positives, Secret Manager und organisatorische Prävention
Entropiebasierte Regeln erzeugen erfahrungsgemäß mehr False Positives als reine Regex-Muster, etwa bei Hash-Werten, Base64-kodierten Testdaten oder Lock-Dateien mit langen, zufällig wirkenden Zeichenketten. Statt Regeln pauschal zu deaktivieren, pflegt man bekannte Ausnahmen in einer .gitleaksignore-Datei, in der jeder erlaubte Fund über seinen eindeutigen Fingerprint referenziert wird. Das hält die eigentliche Regel scharf, ohne das Team mit wiederkehrenden Fehlalarmen zu ermüden.
Technisches Scanning ersetzt keine organisatorische Prävention. Secrets gehören grundsätzlich nicht in Git, sondern in einen dedizierten Secret Manager wie HashiCorp Vault oder AWS Secrets Manager, aus dem sie zur Laufzeit geladen werden. Für lokale Entwicklung etabliert sich das Muster einer eingecheckten .env.example mit Platzhalterwerten neben einer konsequent ignorierten echten .env. Die folgende Tabelle zeigt, welchen Schutzgrad unterschiedliche Scanning-Stufen tatsächlich bieten.
| Szenario | Typische Lücke | Tatsächliche Deckung | Empfehlung |
|---|---|---|---|
| Kein Scanning | Secret fällt erst durch Missbrauch auf | Kein Schutz vor Commit oder Push | Sofort gitleaks oder git-secrets einführen |
| Nur Pre-Commit-Hook | --no-verify umgeht den Hook vollständig | Nur auf Rechnern mit installiertem Hook | Nie als alleinige Verteidigungslinie nutzen |
| Nur CI-Pipeline | Secret ist bereits lokal committet | Verhindert Merge, nicht die lokale Exposition | Mit Pre-Commit-Hook kombinieren |
| Pre-Commit + CI | Zwei Konfigurationen benötigen Pflege | Deckt Entwicklerrechner und jeden Push/PR ab | Als Standard für alle Repositories etablieren |
Mironsoft
Secrets-Scanning, CI/CD-Pipelines und sichere Git-Workflows für PHP- und Magento-Teams
Secrets zuverlässig aus eurer Codebasis heraushalten?
Wir richten gitleaks oder git-secrets als Pre-Commit-Hook und CI-Gate ein, härten bestehende Repositories gegen bekannte Altlasten ab und begleiten euch bei der Umstellung auf einen zentralen Secret Manager.
Repository-Audit
Vollständiger Historien-Scan auf bereits committete Secrets und Altlasten
Hook & CI-Setup
gitleaks oder git-secrets als Pre-Commit-Hook und Pipeline-Gate einrichten
Incident Response
Rotation und Historienbereinigung im Ernstfall koordiniert begleiten
10. Zusammenfassung
Git-Secrets-Scanning löst ein Problem, das reine Disziplin nicht zuverlässig löst: Secrets landen trotz guter Absicht über vergessene .env-Dateien, hardcodierte Konfigurationswerte und Debug-Commits in der Historie, und ein späteres Löschen der Datei entfernt den Wert dort nicht. gitleaks und git-secrets fangen solche Funde als Pre-Commit-Hook lokal ab, kombiniert regex- und entropiebasierte Erkennung deckt sowohl bekannte Formate als auch projektspezifische Tokens ab. Weil lokale Hooks mit --no-verify umgangen werden können und auf frischen Klonen fehlen, ist ein zweiter, CI-basierter Scan auf jedem Push und jeder Pull Request unverzichtbar.
Im tatsächlichen Leak-Fall entscheidet die Reihenfolge: zuerst die Zugangsdaten an der Quelle widerrufen und rotieren, danach die Git-Historie bereinigen. Organisatorisch reduziert ein zentraler Secret Manager zusammen mit dem .env.example-Muster die Zahl der Gelegenheiten, bei denen ein echtes Secret überhaupt in Reichweite eines git add gerät.
Git-Secrets-Scanning: Das Wichtigste auf einen Blick
Prävention vor dem Commit
gitleaks und git-secrets als Pre-Commit-Hooks blockieren Secrets, bevor sie überhaupt lokal committet werden.
Zweite Verteidigungslinie
Ein CI-Job scannt jeden Push und jede Pull Request, unabhängig davon, ob der lokale Hook aktiv war.
Im Ernstfall: Rotation zuerst
Zugangsdaten sofort an der Quelle widerrufen, erst danach die Git-Historie mit filter-repo bereinigen.
Organisatorische Prävention
Secret Manager statt committeter .env-Dateien, .env.example als Platzhalter-Muster im Repository.