Ein API-Token, das versehentlich im Commit landet, ist kein Schönheitsfehler. Git vergisst nichts, und sobald das Repository geteilt oder gespiegelt wird, liegt der Schlüssel dauerhaft in der History. Ein git rm im nächsten Commit hilft nicht, denn der alte Stand bleibt erreichbar. Deshalb lohnt sich eine doppelte Absicherung: ein Scanner, der die History und den aktuellen Stand prüft, und ein Hook, der den Commit gar nicht erst durchlässt.
Warum Secrets in Git so hartnäckig sind
Jeder Commit ist ein unveränderliches Objekt. Wer ein Passwort committet und es später entfernt, erzeugt nur einen neuen Commit obendrauf. Der alte Blob bleibt im Objektspeicher und ist über die Commit-History, über Forks und über jeden Clone abrufbar. Praktisch heißt das: ein einmal exponiertes Secret gilt als kompromittiert und muss rotiert werden. Das Entfernen aus der History ist Schadensbegrenzung, kein Ersatz für einen neuen Schlüssel.
gitleaks: History und Working Tree scannen
gitleaks ist ein einzelnes Binary, das mit einem Satz vordefinierter Regeln nach typischen Secret-Mustern sucht: AWS-Keys, private Schlüssel, generische High-Entropy-Strings und vieles mehr. Der Aufruf für ein lokales Repository ist kurz.
# gesamte Git-History prüfengitleaks detect --source . --verbose# nur den aktuellen Working Tree, ohne Historygitleaks detect --no-git --source .# Befund als Report ausgebengitleaks detect --source . --report-path gitleaks-report.json
Der erste Lauf auf einem gewachsenen Repository fördert oft Treffer zutage, die längst rotiert oder gar nicht echt sind. Falsche Positive lassen sich über eine .gitleaks.toml mit Allowlist-Einträgen ausblenden, etwa für Testdaten oder Beispielschlüssel in der Dokumentation.
# .gitleaks.toml[allowlist]description = "Bekannte unkritische Treffer"regexes = [ '''EXAMPLE_KEY_[0-9A-Z]+''',]paths = [ '''test/fixtures/.*''',]
pre-commit Hooks: der Filter vor dem Commit
Ein Scanner in der Pipeline findet das Secret, nachdem es schon gepusht wurde. Besser ist es, den Commit lokal zu stoppen. Das Framework pre-commit verwaltet Git-Hooks deklarativ über eine Konfigurationsdatei. gitleaks bringt einen passenden Hook mit.
# .pre-commit-config.yamlrepos: - repo: https://github.com/gitleaks/gitleaks rev: v8.18.4 hooks: - id: gitleaks
Einmal installiert, läuft der Hook bei jedem Commit und blockiert ihn, sobald ein Treffer auftaucht.
pip install pre-commitpre-commit install# einmalig gegen alle Dateien testenpre-commit run --all-files
Wichtig ist, dass der Hook für das Team verbindlich ist, nicht nur auf einer Maschine. Die .pre-commit-config.yaml gehört ins Repository, und der Installationsschritt gehört in die Onboarding-Dokumentation. Ein Hook, den nur die Hälfte des Teams aktiviert hat, gibt eine trügerische Sicherheit.
Wenn ein Secret schon committet wurde
Zuerst rotieren, dann aufräumen. Der Schlüssel muss beim Anbieter ungültig gemacht und neu ausgestellt werden, bevor irgendetwas an der History passiert. Erst danach lohnt das Entfernen aus den alten Commits, etwa mit git filter-repo.
# Datei vollständig aus der History entfernengit filter-repo --path config/secrets.yml --invert-paths# danach force-push, alle Klone müssen neu geklont werdengit push --force
Ein Force-Push auf einem geteilten Branch ist ein Eingriff, der das Team betrifft. Alle bestehenden Klone enthalten den alten Stand weiter, deshalb gilt: rotieren ist Pflicht, History-Rewrite ist Kosmetik gegen das versehentliche Wiederfinden.
Den Scan in die Pipeline ziehen
Der lokale Hook fängt das meiste ab, aber er greift nur bei Leuten, die ihn installiert haben. Ein zusätzlicher Scan in der CI schließt die Lücke und macht den Check unabhängig von der lokalen Einrichtung.
# .github/workflows/gitleaks.ymlname: gitleakson: [push, pull_request]jobs: scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: gitleaks/gitleaks-action@v2
Das fetch-depth: 0 ist hier entscheidend, denn ohne vollständige History scannt gitleaks nur den letzten Commit und übersieht ältere Treffer. So entsteht eine Kette: der Hook stoppt lokal, die Pipeline stoppt im Pull Request, und ein regelmäßiger History-Scan deckt Altlasten auf.
Fazit
Secret-Hygiene ist kein einzelnes Werkzeug, sondern eine Reihenfolge: lokaler Hook als erste Linie, CI-Scan als Sicherung, History-Scan gegen Altlasten, und im Ernstfall immer zuerst rotieren. gitleaks und pre-commit decken den größten Teil davon mit wenig Aufwand ab. Wer das Thema breiter aufzieht und Secrets, Build-Pipeline und Lieferkette zusammen denkt, findet weiterführende Praxisbeispiele auf digital-business.blog.
Hinterlasse einen Kommentar