- Sicherheit
- Git
- Secrets
Geleakte Secrets in der Git-Historie: finden, rotieren, verhindern
Ein gelöschter API-Schlüssel bleibt in der Git-Historie. So finden Sie geleakte Secrets, rotieren sie zuerst, bereinigen die Historie und verhindern das nächste Leak.
· 7 Min. Lesezeit · Lina Source LLC
Jemand committet eine .env-Datei oder fügt einen aktiven API-Schlüssel in ein Konfigurationsmodul ein, um schnell etwas zu testen. Ein Reviewer bemerkt es, der Schlüssel wird im nächsten Commit gelöscht, und alle machen weiter. Der Schlüssel ist trotzdem noch da. Git speichert jede Version jeder Datei, und jeder, der das Repository klonen kann, kann den Commit lesen, der ihn hinzugefügt hat.
Hartcodierte Zugangsdaten werden als CWE-798 geführt. Dieser Leitfaden zeigt, was zu tun ist, wenn bereits eines in der Historie gelandet ist: jedes Secret finden, alle vor allem anderen rotieren, entscheiden, ob sich das Umschreiben der Historie lohnt, und Schutzmaßnahmen einrichten, die das nächste verhindern.
Warum das Löschen der Zeile nicht genügt
Ein Commit, der ein Secret entfernt, fügt einen neuen Snapshot ohne dieses Secret hinzu. Der vorherige Snapshot mit dem Secret wird weiterhin von der Historie des Branches referenziert. git log -p zeigt ihn, git checkout des alten Commits stellt ihn wieder her, und jeder bestehende Klon und Fork besitzt bereits eine Kopie. Auch das Squashen des Branches vor dem Merge hilft nicht, wenn die ursprünglichen Commits jemals gepusht wurden: Das Remote kann sie noch enthalten, und jeder, der sie gefetcht hat, hat sie lokal.
Bei einem öffentlichen Repository sollten Sie davon ausgehen, dass das Secret gesehen wurde. Automatisierte Scraper überwachen öffentliche Pushes auf Muster von Zugangsdaten, und die Zeitspanne zwischen einem Push und dem ersten Missbrauch kann sehr kurz sein. Bei einem privaten Repository ist die Exposition geringer, aber nicht null: Jeder Mitarbeiter, jeder Auftragnehmer, jedes CI-System und jede Integration mit Lesezugriff hat es.
Schritt 1: zuerst rotieren
Die Rotation ist der einzige Schritt, der das Risiko tatsächlich beseitigt. Das Umschreiben der Historie verbirgt das Secret vor künftigen Klonen; gegen bereits existierende Kopien bewirkt es nichts. Widerrufen und ersetzen Sie die Zugangsdaten daher, bevor Sie das Repository anfassen.
Planen Sie die Rotation so, dass sie keinen Ausfall verursacht. Viele Anbieter erlauben zwei gleichzeitig gültige Schlüssel: Erstellen Sie den neuen Schlüssel, deployen Sie ihn überall dort, wo der alte verwendet wurde, prüfen Sie, dass der Traffic umgezogen ist, und widerrufen Sie dann den alten. Erlaubt der Anbieter nur einen Schlüssel, nehmen Sie eine kurze Unterbrechung in Kauf; ein paar Minuten Ausfallzeit sind ein besserer Tausch, als bekannt geleakte Zugangsdaten aktiv zu lassen, während Sie eine perfekte Umstellung koordinieren.
- Erzeugen Sie einen neuen Schlüssel in der Konsole des Anbieters und deployen Sie ihn über Ihren normalen Konfigurationsweg.
- Widerrufen Sie den alten Schlüssel. Hören Sie nicht einfach auf, ihn zu verwenden; ein ungenutzter gültiger Schlüssel ist immer noch ein gültiger Schlüssel.
- Prüfen Sie die Audit-Logs des Anbieters auf Aktivitäten mit dem alten Schlüssel seit dem Commit-Datum: API-Aufrufe, neue Benutzer, geänderte Berechtigungen, unerwartete Abrechnungen.
- War das Secret ein Datenbankpasswort oder ein Signaturschlüssel, überlegen Sie, was es freigeschaltet haben könnte. Ein geleaktes JWT-Signatur-Secret bedeutet, dass jedes Token gefälscht worden sein könnte; invalidieren Sie also bestehende Sessions.
- Dokumentieren Sie, was offengelegt wurde, wie lange und was Sie geändert haben. Sie werden es brauchen, wenn ein Kunde oder Auditor nachfragt.
Schritt 2: alles finden, was geleakt wurde
Wo ein committetes Secret ist, sind oft weitere. Durchsuchen Sie die gesamte Historie, nicht nur den aktuellen Stand, und schließen Sie jeden Branch und jedes Tag ein.
# Commits, die einen String irgendwo in der Historie hinzugefügt oder entfernt haben
git log -p -S "sk_live_" --all
# Regex-Suche über die Diffs aller Commits
git log -p -G "AKIA[0-9A-Z]{16}" --all
# Dateien, die jemals unter einem verdächtigen Pfad existiert haben
git log --all --oneline -- .env config/production.json
# Spezialisierte Scanner prüfen Hunderte bekannter Formate von Zugangsdaten
gitleaks git -v .
trufflehog git file://. --only-verifiedgitleaks und trufflehog sind Open-Source-Scanner, die genau für diese Aufgabe gebaut sind. Sie kennen die Formate gängiger Anbieterschlüssel, und trufflehog kann prüfen, ob gefundene Zugangsdaten noch aktiv sind. Ältere gitleaks-Versionen verwenden gitleaks detect --source . statt des Unterbefehls git. Lassen Sie einen Scanner einmal über die gesamte Historie laufen und danach dauerhaft auf neuen Commits. Rechnen Sie mit einigen False Positives, etwa Test-Fixtures und Beispielschlüsseln in der Dokumentation. Prüfen Sie jeden Treffer und tragen Sie bestätigte False Positives in die Allowlist oder Baseline-Datei des Scanners ein, damit der nächste Lauf nur neue Befunde zeigt.
Vergessen Sie nicht die Orte rund um das Repository: CI-Logs, die Umgebungsvariablen ausgegeben haben, Kommentare in Issues und Pull Requests, Wiki-Seiten, Docker-Image-Layer, die aus dem Repository gebaut wurden, sowie Gists oder Pastes, die beim Debuggen geteilt wurden.
Schritt 3: entscheiden, ob Sie die Historie umschreiben
Sobald das Secret rotiert ist, ist es nutzlos; das Umschreiben der Historie ist dann Aufräumarbeit statt Eindämmung. Es lohnt sich trotzdem, wenn das Repository öffentlich ist, wenn das Secret mehr als sich selbst verrät (einen internen Hostnamen, einen Kundennamen) oder wenn Compliance-Vorgaben es verlangen. Es hat echte Kosten: Alle Mitwirkenden müssen neu klonen, offene Pull Requests werden gestört, und Commit-Hashes ändern sich. Alles, was einen Commit per Hash referenziert, etwa Release Notes, Deployment-Protokolle, Issue-Links oder gepinnte Abhängigkeiten in anderen Repositories, zeigt dann auf Commits, die auf dem Branch nicht mehr existieren. Kündigen Sie das Umschreiben vorab an, wählen Sie einen ruhigen Zeitpunkt und mergen oder schließen Sie offene Pull Requests zuerst.
git filter-repo ist das Werkzeug, das das Git-Projekt dafür empfiehlt, anstelle des älteren git filter-branch. Es arbeitet auf einem frischen Klon und entfernt das Remote origin als Sicherheitsmaßnahme, sodass Sie es vor dem Push wieder hinzufügen.
# Mit einem frischen Klon beginnen
git clone [email protected]:acme/api.git api-clean
cd api-clean
# Option A: eine Datei aus jedem Commit entfernen
git filter-repo --invert-paths --path config/production.env
# Option B: Secret-Strings überall ersetzen, wo sie vorkommen
# replacements.txt enthält eine Regel pro Zeile, zum Beispiel:
# PASTE_THE_OLD_KEY_HERE==>REDACTED
git filter-repo --replace-text ../replacements.txt
# filter-repo entfernt origin; wieder hinzufügen und force-pushen
git remote add origin [email protected]:acme/api.git
git push --force --all
git push --force --tagsWas das Umschreiben nicht leisten kann
- Es erreicht keine bestehenden Klone, Forks, CI-Caches oder Backups. Wer vor dem Umschreiben gefetcht hat, behält die alten Commits.
- Auf GitHub sind die für Pull Requests angelegten Referenzen schreibgeschützt und können alte Commits erreichbar halten. Um zwischengespeicherte Ansichten und diese Referenzen zu entfernen, müssen Sie den GitHub-Support kontaktieren.
- Mitwirkende, die aus einem alten Klon pullen oder mergen, können die alte Historie direkt wieder zurückpushen. Bitten Sie alle, neu zu klonen, und schützen Sie den Branch, solange das geschieht.
- Es entfernt das Secret nicht von anderen Orten, an die es kopiert wurde: Logs, Tickets, Chatnachrichten oder gebaute Artefakte.
Deshalb ist die Reihenfolge entscheidend. Erst rotieren, dann aufräumen. Eine umgeschriebene Historie mit einem aktiven Schlüssel vermittelt eine trügerische Sicherheit. Lassen Sie nach dem Push Ihren Scanner über einen frischen Klon laufen, um zu bestätigen, dass das Secret wirklich aus jedem Branch und jedem Tag verschwunden ist.
Schritt 4: das nächste Leak verhindern
Ziel ist es, das Committen eines Secrets schwer und das Bemerken schnell zu machen. Keine einzelne Maßnahme leistet beides, also kombinieren Sie mehrere Schichten.
Secrets aus dem Codepfad heraushalten
Lesen Sie Zugangsdaten zur Laufzeit aus Umgebungsvariablen oder einem Secret-Manager und prüfen Sie beim Start, dass die benötigten vorhanden sind. Committen Sie eine .env.example mit Platzhalterwerten und nehmen Sie .env vom ersten Commit an in die .gitignore auf. Für die Produktion bietet ein Secret-Manager wie AWS Secrets Manager, Google Secret Manager, HashiCorp Vault oder die verschlüsselten Umgebungseinstellungen Ihrer Plattform Zugriffskontrolle, Audit-Logs und eine einfachere Rotation.
Secrets blockieren, bevor sie committet werden
Ein Pre-Commit-Hook fängt den Fehler auf dem Rechner des Entwicklers ab, bevor er überhaupt das Remote erreicht. Mit dem pre-commit-Framework sind das wenige Zeilen Konfiguration, die jeder Mitwirkende installieren kann.
# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: vX.Y.Z # auf das neueste Release-Tag pinnen
hooks:
- id: gitleaks
# Einmal pro Klon installieren
# pip install pre-commit
# pre-commit installHooks laufen lokal und lassen sich überspringen, daher sollte derselbe Scanner als Absicherung bei jedem Push in der CI laufen. Aktivieren Sie auf GitHub Secret Scanning und Push Protection, sofern Ihr Plan sie unterstützt; Push Protection lehnt serverseitig Pushes ab, die erkannte Anbieter-Tokens enthalten.
Den Schaden unentdeckter Leaks begrenzen
- Beschränken Sie jeden Schlüssel auf die minimal nötigen Berechtigungen und, sofern der Anbieter es erlaubt, auf bestimmte IPs oder Referrer.
- Bevorzugen Sie kurzlebige Zugangsdaten, etwa OIDC-Federation von der CI zu Ihrem Cloud-Anbieter, gegenüber langlebigen statischen Schlüsseln.
- Verwenden Sie getrennte Schlüssel pro Umgebung, damit ein geleakter Testschlüssel die Produktion nicht berühren kann.
- Halten Sie für jedes kritische Secret ein Rotations-Runbook bereit, damit eine Rotation unter Druck Routine ist und keine Improvisation.
Welche Rolle Code-Review spielt
Scanner erkennen bekannte Formate gut; bei Kontext sind sie schwächer, etwa bei einem Passwort, das aus zwei Konstanten zusammengesetzt wird, oder bei Standard-Zugangsdaten, die in einem Konfigurations-Fallback mitgeliefert werden. Ein Review des Codes findet solche Fälle. CodeAuditAgent meldet hartcodierte Zugangsdaten als CWE-798 mit zitiertem Beleg, wenn es den Standard-Branch eines öffentlichen Repositorys oder ein eingefügtes Code-Snippet prüft. Es liest den aktuellen Code, nicht die Commit-Historie; behalten Sie für vergangene Commits also einen Historien-Scanner bei.
Kurz gesagt: Ein committetes Secret ist ein geleaktes Secret. Rotieren Sie es noch heute, bereinigen Sie die Historie, wenn es den Aufwand wert ist, und sorgen Sie dafür, dass das nächste Leak am Pre-Commit-Hook scheitert statt auf einer öffentlichen Seite zu landen.