Zugangsschlüssel in der Git-Historie: die richtige Reihenfolge beim Aufräumen
Der erste Reflex ist ein Commit, der die Datei löscht, und der bringt nichts. Was wirklich hilft, ist eine Reihenfolge, in der der Tausch vor dem Aufräumen kommt.
KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
Das Passwort zu löschen fühlt sich richtig an und ändert an der Lage nichts
Der Ablauf ist in fast jedem Fall derselbe. Jemand bemerkt, dass eine Konfigurationsdatei mit einem Zugangsschlüssel mitgecommittet wurde, löscht die Datei, schreibt einen Commit mit einer Nachricht wie Schlüssel entfernt und schiebt ihn hoch. In der aktuellen Ansicht ist nichts mehr zu sehen, und damit gilt die Sache als erledigt. Sie ist es nicht: Der Inhalt liegt weiterhin im Repository und ist über den älteren Commit mit zwei Handgriffen abrufbar. Der Commit mit der beruhigenden Nachricht ist dabei sogar ein Wegweiser darauf, wo genau man suchen muss.
Der zweite Denkfehler betrifft die Reichweite. Ein Repository, das auf einem Server liegt, wurde geklont, gespiegelt, in Zwischenspeicher von CI-Servern geschrieben und, wenn es öffentlich war, mit hoher Wahrscheinlichkeit maschinell durchsucht. Es gibt Dienste, die neu veröffentlichte Commits fortlaufend nach Zeichenketten abgrasen, die wie Zugangsdaten aussehen, und die Zeit zwischen einem Push und einem ersten Zugriffsversuch wird in Minuten gemessen und nicht in Tagen. Was einmal draußen war, holt keine Bereinigung zurück.
Daraus folgt die Reihenfolge, die diese Seite empfiehlt und die im Ernstfall häufig umgedreht wird. Zuerst wird der Schlüssel ungültig gemacht und ersetzt, denn das ist die einzige Maßnahme, die den Zugriff tatsächlich verhindert. Danach wird geprüft, ob mit dem Schlüssel bereits etwas passiert ist. Erst an dritter Stelle steht das Umschreiben der Historie, und es dient dazu, den Fund nicht weiter zu verbreiten und die eigene Sorgfaltspflicht zu erfüllen, nicht dazu, den Schaden abzuwenden.
KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
Die Fälle, die im Alltag wirklich auflaufen
Sortiert nach dem, was ihr vorfindet. Der Unterschied zwischen einem harmlosen und einem teuren Fall liegt fast immer darin, ob der Stand schon auf einem Server war und wer ihn seitdem geholt hat.
Symptom
Der Schlüssel steckt im letzten Commit, und der ist noch nicht auf dem Server.
Ursache
Ein pauschales Hinzufügen aller Dateien hat eine lokale Konfigurationsdatei mitgenommen, die in der .gitignore fehlt. Das ist der günstigste Fall, weil der Inhalt euer Gerät nie verlassen hat.
Lösung
Nimm die Datei mit git rm --cached aus der Verwaltung, trag sie in die .gitignore ein und schreib den letzten Commit mit git commit --amend neu. Prüf danach mit git log --branches --not --remotes, ob wirklich nichts davon bereits hochgeladen wurde. Ein Tausch des Schlüssels ist hier verzichtbar, sofern das Gerät verschlüsselt ist und niemand sonst darauf zugreift, und im Zweifel tauscht ihr trotzdem.
Symptom
Der Wert steht in einem älteren Commit und wurde in einem neueren gelöscht, alles davon ist längst auf dem Server.
Ursache
Ein Commit ist unveränderlich, und der alte Inhalt bleibt als eigenes Objekt im Repository liegen, solange ein Commit darauf verweist. Das Löschen erzeugt nur einen weiteren Commit obendrauf, es entfernt nichts aus der Vergangenheit.
Lösung
Schlüssel tauschen, dann den Umfang bestimmen und erst danach die Historie mit git filter-repo auf einem frischen Klon umschreiben. Plant das Zeitfenster mit dem Team, weil sich sämtliche Commit-IDs ändern und danach niemand mehr ziehen kann, sondern alle neu klonen müssen.
Symptom
Das Repository ist öffentlich oder war es zwischenzeitlich.
Ursache
Öffentliche Stände werden maschinell durchsucht. Es existieren Dienste, die neue Commits fortlaufend nach Mustern für Zugangsdaten absuchen, und außerdem Klone, Spiegel und Archive, an die ihr technisch nicht herankommt.
Lösung
Behandelt es als Vorfall und nicht als Aufräumarbeit. Der Tausch des Schlüssels ist die einzige Maßnahme mit Wirkung, das Umschreiben der Historie ist Kosmetik und Sorgfaltspflicht. Prüft die Zugriffsprotokolle für den gesamten Zeitraum und dokumentiert das Ergebnis, weil bei personenbezogenen Daten daran eine Meldepflicht hängen kann.
Symptom
Die Historie ist bereinigt, und trotzdem lässt sich der alte Inhalt über einen Link auf der Plattform noch abrufen.
Ursache
Die Plattformen halten Objekte, die aus keinem Branch mehr erreichbar sind, noch eine Weile vor, unter anderem für die Ansicht alter Beiträge und für Vergleiche. Dazu kommen Forks, die sich mit dem Ursprung ein gemeinsames Objektlager teilen können.
Lösung
Das bekommt ihr nicht allein aufgeräumt. Wendet euch an den Support eurer Plattform und lasst die zwischengespeicherten Ansichten entfernen, und klärt gleichzeitig, welche Forks existieren und wer sie löschen kann. Bis das erledigt ist, bleibt der getauschte Schlüssel eure einzige wirksame Maßnahme.
Symptom
Der Wert taucht nicht nur im Code auf, sondern auch in Protokollen der Pipeline und in abgelegten Artefakten.
Ursache
Ein Skript hat die Umgebung ausgegeben, ein Werkzeug lief mit ausführlicher Protokollierung, oder eine Konfigurationsdatei wurde als Artefakt mitgesichert. Solche Kopien überleben das Umschreiben der Historie, weil sie gar nicht im Repository liegen.
Lösung
Löscht die betroffenen Läufe und Artefakte, hinterlegt Zugangsdaten künftig als geschützte Variablen, damit sie in der Ausgabe maskiert werden, und schaltet die ausführliche Protokollierung dort ab, wo sie an Werkzeuge mit Zugangsdaten gerät. Begrenzt außerdem die Aufbewahrungsdauer von Protokollen, dann wird dieser Fall mit der Zeit von selbst kleiner.
Symptom
Es passiert immer wieder, mal ein Token in einem Notebook, mal ein Connection String in einer Testdatei.
Ursache
Es gibt keinen vorgesehenen Ort für Geheimnisse, deshalb landen sie dort, wo gerade gearbeitet wird. Solange die Anwendung ihre Zugangsdaten aus einer Datei im Projekt liest, ist der nächste Fund nur eine Frage der Zeit.
Lösung
Baut die Anwendungen so um, dass sie Zugangsdaten aus der Umgebung oder aus einem zentralen Speicher beziehen, und legt eine Beispieldatei ohne echte Werte ins Repository, damit klar ist, welche Schlüssel erwartet werden. Ergänzt eine automatische Prüfung vor dem Commit und die Prüfung beim Push auf der Plattform, dann fällt der Fehler auf, bevor er in der Historie steht.
Fünf Schritte in der richtigen Reihenfolge
- 01 Schlüssel widerrufen und neu ausstellen, bevor irgendetwas anderes passiert.
- 02 Alle Branches und Tags danach durchsuchen, seit wann der Wert im Repository steht.
- 03 Zugriffsprotokolle für diesen Zeitraum prüfen und das Ergebnis festhalten.
- 04 Historie auf einem frischen Klon umschreiben, danach alle neu klonen lassen.
- 05 Secret Scanning beim Push einschalten und Secrets aus dem Repository heraushalten.
Die Reihenfolge, nach der ihr im Ernstfall vorgeht
Sechs Schritte, und der erste davon duldet keinen Aufschub. Wer mit dem Umschreiben der Historie beginnt, verliert genau die Zeit, in der der Schlüssel noch gültig ist.
Den Schlüssel sofort ungültig machen
Zurückziehen, sperren, neu ausstellen, in dieser Reihenfolge. Bei einem Zugangstoken heißt das widerrufen, bei einem Datenbankkonto Passwort ändern, bei einem Zertifikat sperren und neu ausstellen. Erst wenn das erledigt ist, lohnt sich die Beschäftigung mit der Historie.
Den Umfang bestimmen
Mit git log -p -S und der Zeichenkette findest du jeden Commit, in dem der Wert hinzugefügt oder entfernt wurde. Prüf dabei alle Branches und Tags, nicht nur den Hauptstand, und denk an Branches, die nur lokal auf Rechnern von Kollegen existieren. Halt fest, seit wann der Wert im Repository steht, denn diese Zeitspanne bestimmt den Umfang der Prüfung im nächsten Schritt.
Nachsehen, ob der Schlüssel benutzt wurde
Zieh die Zugriffsprotokolle des betroffenen Systems für den gesamten Zeitraum und such nach Zugriffen aus unbekannten Netzen oder zu ungewöhnlichen Zeiten. Dieser Schritt entscheidet darüber, ob ihr einen Fund aufräumt oder einen Vorfall bearbeitet, und er gehört dokumentiert, weil die Frage später mit Sicherheit gestellt wird.
Die Historie umschreiben
Das Werkzeug der Wahl ist git filter-repo, das auf einem frischen Klon arbeitet und die betroffenen Inhalte aus allen Commits entfernt. Der ältere Weg über git filter-branch wird in der Dokumentation von Git selbst nicht mehr empfohlen, weil er langsam und fehleranfällig ist. Rechne fest damit, dass sich anschließend alle Commit-IDs ändern.
Das Team und die Plattform mitnehmen
Kündigt ein Zeitfenster an, in dem niemand pusht, führt das Umschreiben durch und lasst danach alle neu klonen statt ziehen. Offene Beiträge müssen neu erstellt werden, Tags neu gesetzt. Für Forks, zwischengespeicherte Ansichten und die Darstellung alter Beiträge braucht ihr zusätzlich den Support eurer Plattform, weil ihr an diese Kopien selbst nicht herankommt.
Den nächsten Fall verhindern
Schaltet die Prüfung beim Push auf der Plattform ein, damit erkannte Zugangsdaten gar nicht erst angenommen werden, und ergänzt eine Prüfung vor dem Commit auf den Arbeitsplätzen. Dazu gehören eine gepflegte .gitignore, eine Beispieldatei ohne echte Werte im Repository und ein zentraler Secrets-Speicher, aus dem die Anwendung sie zur Laufzeit bezieht.
Auf kurzlebige Zugangsdaten umstellen
Der wirksamste Schutz ist ein Schlüssel, der nach einer Stunde ohnehin wertlos ist. Wo eure Umgebung kurzlebige Token oder eine Anmeldung über die Identität des Dienstes erlaubt, verliert der ganze Vorgang seinen Schrecken, weil ein abgeflossener Wert nicht mehr benutzbar ist, bevor jemand ihn ausprobiert.
KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
Warum das Löschen im nächsten Commit nichts nützt
Git speichert nicht Änderungen, sondern vollständige Stände. Jede Version einer Datei liegt als eigenes Objekt im Repository, adressiert über eine Prüfsumme ihres Inhalts. Ein Commit verweist auf einen Baum, der Baum auf diese Objekte, und weil ein Commit unveränderlich ist, bleibt jedes Objekt erreichbar, solange irgendein Commit darauf zeigt. Ein neuer Commit, der die Datei löscht, entfernt sie aus dem aktuellen Stand und aus keinem einzigen früheren.
Praktisch heißt das: Wer die ID des alten Commits kennt oder einfach den Verlauf der Datei anschaut, hat den Inhalt in Sekunden vor sich. Der Commit mit der Nachricht, dass ein Schlüssel entfernt wurde, ist dabei die beste Suchhilfe, die man einem Angreifer geben kann, weil er genau die Stelle markiert. Aus demselben Grund ist es sinnvoll, den Umfang mit einer Suche über den Inhalt zu bestimmen, etwa mit git log -p -S und dem Wert selbst, statt sich auf die Nachrichten zu verlassen.
Der einzige Weg, ein Objekt wirklich loszuwerden, führt deshalb über neue Commits ohne dieses Objekt, also über das Umschreiben der Historie. Und selbst danach liegen die alten Objekte lokal noch herum, bis die Aufräumfunktion sie entfernt, weshalb der übliche Rat lautet, nach dem Umschreiben mit einem frischen Klon weiterzuarbeiten statt mit dem bearbeiteten Verzeichnis.
Historie umschreiben: was das für alle anderen bedeutet
Die ID eines Commits ist eine Prüfsumme über seinen Inhalt und über seinen Vorgänger. Ändert sich irgendwo in der Vergangenheit etwas, ändern sich damit auch alle IDs danach. Ein Umschreiben tauscht also nicht ein paar Commits aus, sondern erzeugt eine vollständig neue Historie, die zufällig fast denselben Inhalt hat. Für alle Beteiligten ist das dasselbe wie ein neues Repository unter dem alten Namen.
Daraus folgt der organisatorische Teil, der meistens unterschätzt wird. Offene Beiträge auf der Plattform verlieren ihren Bezug und müssen neu gestellt werden. Tags müssen neu gesetzt werden. Branches, die noch auf Rechnern liegen, passen nicht mehr und dürfen auf keinen Fall zurückgeschoben werden, weil sie die entfernten Objekte sonst wieder mitbringen. CI-Server, Spiegel und alles, was das Repository automatisiert klont, braucht ebenfalls einen neuen Stand.
Der übliche Ablauf ist deshalb ein angekündigtes Zeitfenster: Alle schieben ihre Arbeit hoch, danach wird das Repository für Schreibzugriffe gesperrt, das Umschreiben läuft auf einem frischen Klon, das Ergebnis wird hochgeladen, und anschließend klonen alle neu. Bei einem Repository mit langer Historie und vielen Beteiligten ist das ein halber Tag Arbeit, und genau deshalb lohnt es sich, den Aufwand einmal zu erleben, bevor er unter Druck stattfindet.
Welches Werkzeug wofür
git filter-repo ist heute das Mittel der Wahl. Es ersetzt Inhalte und Pfade über die gesamte Historie, arbeitet deutlich schneller als der alte Weg und besteht bewusst darauf, auf einem frischen Klon zu laufen, damit niemand versehentlich sein Arbeitsverzeichnis zerlegt. Es ist kein Bestandteil der Git-Installation und muss zusätzlich installiert werden.
Der BFG Repo-Cleaner ist die schlankere Alternative für den Standardfall. Er kann genau zwei Dinge, nämlich Dateien nach Muster entfernen und Zeichenketten in allen Ständen ersetzen, und das erledigt er sehr schnell. Wenn ihr eine Liste von Werten habt, die überall verschwinden sollen, ist er oft der kürzere Weg. Für Umbauten, die über Suchen und Ersetzen hinausgehen, greift ihr zum ersten Werkzeug.
git filter-branch steht zwar noch in vielen älteren Anleitungen, wird in der Dokumentation von Git aber ausdrücklich nicht mehr empfohlen. Es ist langsam, es gibt mehrere Fallstricke, die zu unvollständigen Ergebnissen führen, und für den Fall, um den es hier geht, ist ein unvollständiges Ergebnis das schlechteste Ergebnis. Wenn ihr eine Anleitung findet, die damit arbeitet, ist sie vermutlich auch in anderen Punkten veraltet.
Was auf der Plattform übrig bleibt
Nach dem Hochladen der neuen Historie sind die alten Commits aus keinem Branch mehr erreichbar. Erreichbar bedeutet aber nicht abrufbar: Die Plattformen halten unerreichbare Objekte noch eine Weile vor, unter anderem damit alte Beiträge und Vergleiche weiterhin angezeigt werden können. Wer die alte Commit-ID kennt, und die steht zum Beispiel in einer alten Benachrichtigung per E-Mail, kann den Inhalt unter Umständen weiterhin aufrufen.
Der zweite Punkt sind Forks. Ein Fork teilt sich mit dem Ursprung häufig einen gemeinsamen Objektspeicher, und ein Umschreiben im Ursprung räumt im Fork nichts weg. Klärt deshalb, welche Forks es gibt, wem sie gehören und wer sie löschen kann. Bei einem internen Repository ist das eine Rundmail, bei einem öffentlichen mit vielen Forks ist es aussichtslos, und dann bleibt es beim getauschten Schlüssel.
Für beide Punkte gilt: Ihr könnt sie nicht selbst erledigen, aber ihr könnt sie anstoßen. Der Support der Plattform kann die zwischengespeicherten Ansichten entfernen, und dafür braucht er die konkreten IDs und Zeitpunkte. Genau deshalb lohnt es sich, die Suche nach dem Umfang aus Schritt zwei sauber zu dokumentieren, statt sie nebenbei zu erledigen.
Vorbeugen ist der Teil, der wirklich hilft
Die wirksamste Maßnahme ist eine Prüfung, die den Push zurückweist, sobald etwas wie ein Zugangsschlüssel darin steckt. Die großen Plattformen bieten das an, und die Erkennung ist bei Anbieter-Token wie denen von Cloud-Diensten sehr zuverlässig, weil diese Werte ein festes Format haben. Bei einem selbstvergebenen Datenbankpasswort ist sie es naturgemäß nicht, deshalb bleibt eine zweite Verteidigungslinie nötig.
Die zweite Linie sitzt auf dem Arbeitsplatz, als Prüfung vor dem Commit. Damit fällt der Fund auf, bevor er überhaupt in der Historie steht, und der ganze Vorgang aus dieser Seite entfällt. Wichtig ist, dass diese Prüfung Teil der Projekteinrichtung ist und nicht eine Anleitung, die jeder selbst befolgen soll, denn sonst hat sie nach vier Wochen die Hälfte des Teams nicht.
Die dritte Linie ist die Bauweise der Anwendung. Wenn Zugangsdaten aus der Umgebung oder aus einem zentralen Speicher kommen und im Repository nur eine Beispieldatei ohne echte Werte liegt, gibt es schlicht nichts zu committen. Wo eure Umgebung kurzlebige Token unterstützt, ist der Schritt danach noch wirksamer: Ein Wert, der nach einer Stunde abläuft, ist am nächsten Tag kein Vorfall mehr, sondern eine Randnotiz.
Dazu passende Kurse
Wenn ihr das Umschreiben einer Historie einmal geübt haben wollt, bevor es unter Druck stattfindet, sind Git-Kurse zum Umgang mit Zugangsdaten im Repository der richtige Ort dafür.
Weil der Tausch eines Schlüssels nur der Anfang der Aufarbeitung ist, gehören die Kurse zur IT-Sicherheit im Entwicklungsalltag zum selben Thema.
Wie sicher bist du beim Thema wirklich?
Lesen fühlt sich schnell nach Können an. Ein kurzer Test zeigt dir, was davon schon sitzt und wo sich ein Kurs lohnt. Kostenlos, ohne Anmeldung, mit einer Erklärung zu jeder Antwort.
Wo du genau das übst
Sehr umfangreicher Überblick über Zusammenhänge und Anwendungsbereiche. Sehr zu empfehlen.
Effektives, gemeinsames Erarbeiten der Aufgaben mit vielen praktischen Tipps.
Sehr guter Trainer und gute Aufbereitung der Inhalte und auch eingehen auf Fragen der Beteiligten.
Häufige Fragen
Reicht es nicht, den Commit zu löschen und neu zu pushen?
Müssen wir den Schlüssel wirklich tauschen, wenn das Repository nur intern war?
Was ändert sich für das Team nach dem Umschreiben?
Welches Werkzeug sollen wir nehmen?
Wie finden wir heraus, ob der Schlüssel benutzt wurde?
Passt thematisch dazu
Wenn ihr belegen müsst, ob der Schlüssel benutzt wurde, entscheidet die erste Stunde, denn wie Spuren gesichert werden, bevor jemand aufräumt , ist eine eigene Disziplin.
Deine Ansprechpartner
Du bist dir nicht sicher, welcher Kurs oder welches Level zu dir passt? Wir beraten dich persönlich und kostenlos.
Yves Hoppe
Weiterbildung & Beratung
Hilft dir, aus dem Programmierung-Programm den passenden Kurs für deinen Stand zu finden.
Norbert Jansen
Beratung & Inhouse
Plant mit dir Inhouse-Trainings, die auf eure Abläufe und euren Datenbestand zugeschnitten sind.
Den Ernstfall einmal ohne Ernstfall durchspielen
Bei cmt gehst du das Umschreiben einer Historie an einem Repository durch, bei dem nichts kaputtgehen kann, und richtest die Prüfungen ein, die den nächsten Fund verhindern.
Passt dazu
- Merge-Konflikte auflösen, ohne fremde Arbeit zu überschreiben
- Merge oder Rebase: welche Historie euer Team später lesen kann
- Git-Flow, Trunk-Based oder GitHub-Flow: welches Branching-Modell zu eurem Release-Takt passt
- Code Reviews einführen, ohne dass sie zum Nadelöhr werden