Ich habe mein Projekt grundlegend durchgesehen, auf der Suche nach Vergessenem. Dabei fand ich etwas, das ich lieber nicht gefunden hätte: eine Deployment-Konfigurationsdatei mit dem Schlüssel, der die Login-Tokens signiert, dem Admin-Passwort und dem Passwort für den E-Mail-Versand. Im Klartext, in der Git-Historie, auf drei Branches.
Wie es dazu kam
Die Datei stand in der .gitignore. Der Haken: Sie kam dort erst hinein, nachdem sie bereits einmal committet worden war – und .gitignore ignoriert nur Dateien, die noch nicht verfolgt werden. Eine bereits verfolgte Datei bleibt verfolgt, Änderung für Änderung, auch wenn ihr Name in der Liste steht.
Das Repository war privat, aber privat heißt nicht sicher: Mitwirkende, CI-Dienste, Backups und jeder Rechner, auf den es geklont wurde, haben Zugriff darauf.
Aus der gesamten Historie entfernen
Die Datei mit einem neuen Commit zu löschen, hilft nicht: Sie steckt weiterhin in allen früheren Commits. Man muss die Historie jedes Branches umschreiben. Ich habe git filter-repo auf einem Mirror-Klon verwendet, dann einen Force-Push gemacht und geprüft, dass kein Commit sie mehr enthält:
# 1. Stop tracking the file (it stays on disk)
git rm --cached .env.deploy
git commit -m "Stop tracking .env.deploy"
# 2. Rewrite the history of EVERY branch on a mirror clone
git clone --mirror git@github.com:me/my-repo.git
cd my-repo.git
git filter-repo --path .env.deploy --invert-paths
git push --force --mirror
# 3. Verify: no commit on any branch may still contain it
git log --all --oneline -- .env.deploy
Nach so einem Umschreiben muss jeder mit einer lokalen Kopie diese an das neue Remote angleichen: Die alten Commits dürfen nie wieder gepusht werden.
Warum das nicht reicht
Das ist die wichtigste Lektion. Selbst nach dem Umschreiben können alte Commits erreichbar bleiben: GitHub zum Beispiel behält interne Verweise auf Pull Requests, die ein Force-Push nicht löschen kann. Und man weiß nicht, wer das Repository in der Vergangenheit heruntergeladen hat.
Ein Geheimnis aus der Historie zu entfernen, verhindert nur, dass es schlimmer wird; lösen lässt sich das Problem nur, indem man es austauscht. Konkret:
- Ein neuer Signaturschlüssel für Tokens: Er macht alle bestehenden Sitzungen ungültig, alle müssen sich neu anmelden. Das ist der Preis.
- Ein neues Admin-Passwort.
- Das App-Passwort für den Mailversand widerrufen und ein neues erzeugen.
- Die Variablen aktualisieren – in jeder Umgebung, in der sie verwendet werden.
Beim zweiten Mal: dasselbe Muster
Ein paar Wochen später fand ich dasselbe Problem in anderer Form: ein sehr praktisches Server-Setup-Skript, das die Zugangsdaten direkt in die Konfigurationsdatei schrieb. Das ist das wiederkehrende Muster: das Bequemlichkeitsskript, das Geheimnisse enthält, damit man nichts konfigurieren muss. Ich habe es so umgeschrieben, dass es Schlüssel bei der Ausführung erzeugt und die Passwörter abfragt:
# Before: secrets written into a script tracked by git
# JWT_SECRET=hardcoded-value...
# After: generated or asked at runtime, never committed
JWT_SECRET="$(openssl rand -base64 48)"
read -rsp "SMTP password: " SMTP_PASS; echo
umask 077 # the .env file is readable by its owner only
printf 'JWT_SECRET=%s\nSMTP_PASS=%s\n' "$JWT_SECRET" "$SMTP_PASS" > .env
Gewohnheiten, die eine Wiederholung verhindern
- Eine committete
.env.examplemit Variablennamen und ohne Werte; die echte.envkommt nie ins Repository. - Ein Secret-Scanner vor jedem Commit, etwa gitleaks in einem Pre-Commit-Hook: Er blockiert den Commit, bevor Schaden entsteht.
- Der Push-Schutz von GitHub und GitLab, der Pushes mit erkennbaren Geheimnissen ablehnt.
- Nie Geheimnisse in Skripten: zur Laufzeit erzeugen oder aus der Umgebung lesen.
- Eine regelmäßige Prüfung der Historie: Ein Geheimnis, das du selbst findest, ist ein kleiner Vorfall – eines, das andere finden, nicht.
Fazit
Ein Geheimnis in einem Repository ist ab dem Moment, in dem du es findest, als kompromittiert zu betrachten. Die Historie umzuschreiben ist notwendig, aber nicht ausreichend: Die eigentliche Korrektur besteht darin, jede offengelegte Zugangsinformation auszutauschen. Danach lohnt es sich, eine Wiederholung unmöglich zu machen – mit Scannern und klaren Regeln. Weitere Sicherheitspraktiken habe ich in diesem Leitfaden gesammelt.