Was es ist und warum es zählt
Wenn du einen Dienst mit deiner App verbindest, etwa eine Datenbank, Zahlungen oder eine KI, bekommst du einen Schlüssel. Das ist eine lange Zeichenkette, und am bequemsten ist es, sie direkt in den Code zu kleben. Wenn du sie Claude im Chat gibst, ohne zu sagen, wohin sie gehört, macht es das manchmal auch so.
Das Problem ist, wo der Code landet. Wenn du ihn auf GitHub hochlädst, reist der Schlüssel mit. In einem öffentlichen Repository kann ihn jeder lesen, und es gibt Programme, die nur dafür laufen, Schlüssel einzusammeln, sobald sie auftauchen. Wer ihn findet, benutzt ihn in deinem Namen.
Und ihn aus dem Code zu löschen reicht nicht. Git behält die ganze Geschichte: Wenn der Schlüssel in einem Commit war, auch nur in einem, auch vor Monaten, ist er immer noch da. Wer das Repository herunterlädt, findet ihn wieder.
Nicht alle Schlüssel sind geheim. Der öffentliche von Supabase oder Firebase ist dafür gemacht, in der App zu stehen: Darüber schreibe ich in der Anleitung PORTA (strikt.fit/porta). Hier geht es um die anderen: den geheimen Schlüssel der Datenbank, den der KI, der Zahlungen, der E-Mails. Die liegen nur auf dem Server, wie in der Anleitung LIMITE (strikt.fit/limite).
1. Die Prüfung
Bevor du etwas reparierst, schau, was da ist. Öffne Claude Code im Projektordner und füge ein:
Durchsuche das Projekt und die gesamte Git-Historie nach Schlüsseln, Tokens und Passwörtern, die im Code stehen, auch in Konfigurationsdateien und alten Commits. Nenn mir für jeden Fund Datei, Zeile (oder Commit) und Dienst. Gib den Wert nie aus.
„Die gesamte Historie“, sonst schaut Claude nur die aktuellen Dateien an. „Gib den Wert nie aus“, weil es reicht zu wissen, wo er steht: Der Schlüssel soll nicht im Chat auftauchen.
Eine zweite Meinung: gitleaks
Ein Programm, das genau dafür gemacht ist, erkennt die Form sehr vieler Schlüsseltypen. Ich nutze gitleaks, das ist Open Source. Auf dem Mac installierst du es mit Homebrew:
brew install gitleaks
Dann, im Projektordner:
gitleaks git -v --redact gitleaks dir -v --redact
- gitleaks git durchsucht die gesamte Historie, Commit für Commit.
- gitleaks dir durchsucht die aktuellen Dateien, auch die, die Git nicht verfolgt.
- --redact versteckt die Werte in den Ergebnissen, -v zeigt die Details.
Auf GitHub
In öffentlichen Repositories ist das Secret Scanning von GitHub kostenlos und automatisch: Findet es den Schlüssel eines Dienstes, den es kennt, legt es eine Warnung im Tab Security and quality an. Die Push Protection geht weiter: Sie blockiert einen Push, der einen Schlüssel enthält, bevor er online landet. Für deine eigenen Pushes in öffentliche Repositories ist sie standardmäßig an; im Repository schaltest du sie unter Settings → Advanced Security ein. Bei privaten Repositories kann dafür eine kostenpflichtige GitHub-Funktion nötig sein.
2. Die Schlüssel in .env
Der richtige Ort für einen Schlüssel ist eine .env-Datei, die Git nie hochlädt. Das geht so:
- Die Schlüssel kommen in eine .env-Datei im Projektordner: SCHLUESSEL_NAME=wert.
- Füge in der Datei .gitignore die Zeile .env hinzu: Git lädt sie nie hoch.
- Im Code bleibt nur der Name: process.env.SCHLUESSEL_NAME (dein Framework oder dotenv liest die Datei).
- Dort, wo du die App veröffentlichst, kommen dieselben Variablen in die Einstellungen des Hostings.
Der Prompt, mit dem Claude es erledigt:
Hol die Schlüssel aus dem Code: Leg sie in .env ab, füge .env zur .gitignore hinzu, erstelle eine .env.example nur mit den Namen und prüfe, dass kein geheimer Schlüssel im Browser-Code landet.
Die .env.example lädst du dagegen hoch: Sie enthält nur die Namen, etwa AI_API_KEY= ohne etwas dahinter. So wissen du und alle, die mit dir arbeiten, auch später, welche Schlüssel gebraucht werden, ohne einen davon zu sehen.
Wenn .env schon im Repository war
Die .gitignore gilt nur für Dateien, die Git noch nicht verfolgt. Wenn .env schon hochgeladen war, reicht die .gitignore nicht: Führe git rm --cached .env aus, das die Datei aus dem Repository nimmt und auf dem Computer lässt, und mach dann einen Commit. Danach wechsle die Schlüssel, die darin standen, denn sie bleiben in der Historie: siehe Teil 4. Wenn du willst, bitte Claude, das zu machen und dir jeden Schritt zu erklären.
3. Die Variablen, die im Browser landen
Hier stolpert man am häufigsten. Einen Schlüssel in .env zu legen macht ihn nicht geheim: Entscheidend ist, wo der Code läuft, der ihn benutzt. Was auf dem Server läuft, bleibt deins. Was im Browser oder in der App auf dem Handy ankommt, sieht jeder, .env hin oder her.
Manche Frameworks schicken Variablen mit einem bestimmten Präfix absichtlich in den Browser:
- Next.js: NEXT_PUBLIC_. Der Wert wird in das JavaScript geschrieben, das im Browser ankommt.
- Vite: VITE_. Die Vite-Dokumentation sagt ausdrücklich, dass dort keine Schlüssel hingehören.
- Expo: EXPO_PUBLIC_. Der Wert steht im Klartext in der kompilierten App.
Ein geheimer Schlüssel mit einem dieser Präfixe ist öffentlich, auch wenn er in .env steht. Das passiert so: Der KI-Aufruf aus dem Browser funktioniert nicht, das Präfix kommt dazu, jetzt geht es, und der Schlüssel ist draußen. Die richtige Lösung ist, den Aufruf auf den Server zu verlegen.
Wenn Claude .env nicht lesen soll
Claude Code kann die Dateien im Ordner lesen, .env eingeschlossen. Wenn du das nicht willst, füge eine Sperrregel in .claude/settings.json im Projektordner hinzu:
{
"permissions": {
"deny": ["Read(./.env)"]
}
}Trag sie ein, nachdem du die Schlüssel verschoben hast: Sie blockiert auch das Schreiben in diese Datei. Sie gilt für die Werkzeuge, mit denen Claude Dateien liest, und für Befehle wie cat, nicht für ein Skript, das die Datei selbst öffnet: Dafür brauchst du die Sandbox von Claude Code.
4. Wenn dir schon einer entwischt ist
Wenn du einen geheimen Schlüssel in einem Commit, auf GitHub oder im Code für den Browser findest, betrachte ihn als verbrannt. Du weißt nicht, wer ihn schon kopiert hat. Heute, in dieser Reihenfolge:
- Widerrufe den Schlüssel auf der Website des Dienstes und erstelle einen neuen. Das ist der Schritt, der zählt: Wer ihn kopiert hat, kann ihn noch benutzen.
- Leg den neuen nur in .env und in die Einstellungen des Hostings.
- Prüf beim Dienst, ob es ungewöhnlichen Verbrauch gibt.
- Dann räum in Ruhe die Historie auf.
Wenn die App online ist, kannst du den neuen Schlüssel kurz vor dem Widerruf des alten erstellen, damit sie nicht ausfällt. Aber den alten widerrufst du heute, nicht morgen. Für den letzten Schritt bitte erst um eine Erklärung:
Ein Schlüssel ist in einem alten Commit gelandet. Erklär mir, wie ich ihn aus der Git-Historie entferne, bevor du irgendetwas anfasst.
Warum diese Reihenfolge. Einen Schlüssel aus der Historie zu entfernen heißt, die Geschichte des Repositorys neu zu schreiben (GitHub empfiehlt git-filter-repo) und per Force Push hochzuladen, und alte Kopien und Forks enthalten ihn trotzdem noch. Deshalb nennt GitHub als ersten Schritt, den Schlüssel zu widerrufen, und sagt, dass das manchmal reicht: Ein widerrufener Schlüssel öffnet nichts mehr.
Häufige Fehler
- Den Schlüssel aus dem Code löschen und es dabei belassen. Er bleibt in der Historie: Widerruf ihn und erstelle einen neuen.
- Die Historie aufräumen, bevor der Schlüssel widerrufen ist. Widerruf ihn sofort: Das Aufräumen kommt danach.
- Die .env in die .gitignore eintragen, wenn sie schon hochgeladen war. Nimm sie mit git rm --cached .env und einem Commit heraus und wechsle dann die Schlüssel, die darin standen.
- Einen geheimen Schlüssel in eine Variable mit NEXT_PUBLIC_, VITE_ oder EXPO_PUBLIC_ legen. Er landet im Browser oder in der App: Verlege den Aufruf auf den Server.
- Neue Schlüssel in den Chat mit Claude kleben. Trag sie selbst in .env ein und sag Claude nur den Namen der Variable.
Checkliste
- Prüfung von Dateien und Historie gemacht, mit Claude und mit gitleaks.
- Geheime Schlüssel liegen nur in .env und in den Einstellungen des Hostings.
- .env steht in der .gitignore und nicht im Repository.
- Es gibt eine .env.example mit den Namen, ohne Werte.
- Kein geheimer Schlüssel in Variablen mit NEXT_PUBLIC_, VITE_ oder EXPO_PUBLIC_.
- Jeder entwischte Schlüssel ist widerrufen und ersetzt.
- Push Protection im GitHub-Repository eingeschaltet.
Häufige Fragen
Mein Repository ist privat. Muss ich das trotzdem machen?
Ja. Wenn du es eines Tages öffentlich machst oder jemanden hinzufügst, kommt die Historie komplett mit, Schlüssel eingeschlossen. Und Variablen, die im Browser landen, sind sowieso öffentlich.
Ist der öffentliche Schlüssel von Supabase in einer NEXT_PUBLIC_-Variable in Ordnung?
Ja, er ist dafür gemacht, im Browser zu stehen. Die Daten schützen die Regeln auf der Datenbank, wie ich in der Anleitung PORTA erkläre. Der geheime Schlüssel (secret oder service_role) dagegen nie.
Ich habe den Schlüssel schon widerrufen. Muss ich die Historie trotzdem aufräumen?
Nicht immer: Ein widerrufener Schlüssel öffnet nichts mehr. Wenn noch etwas anderes Sensibles in der Historie steht, etwa Daten von Personen, oder du nicht sicher bist, alles widerrufen zu haben, räum auf, angefangen mit dem Prompt aus Teil 4.