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üfen
gitleaks detect --source . --verbose
# nur den aktuellen Working Tree, ohne History
gitleaks detect --no-git --source .
# Befund als Report ausgeben
gitleaks 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.yaml
repos:
- 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-commit
pre-commit install
# einmalig gegen alle Dateien testen
pre-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 entfernen
git filter-repo --path config/secrets.yml --invert-paths
# danach force-push, alle Klone müssen neu geklont werden
git 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.yml
name: gitleaks
on: [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

Diese Seite verwendet Akismet, um Spam zu reduzieren. Erfahre, wie deine Kommentardaten verarbeitet werden..