Blog/Sicherheit & Compliance
Coding-Agenten und Secrets: Schlüssel aus Kontext, Logs und Commits
Wie Secrets über Coding-Agenten abfließen und welche Kontrollen sie stoppen: Deny-Regeln, Sandbox, Pre-Commit-Scans, Push-Schutz, OIDC und Rotation.
Balázs Csorba··11 Min. Lesezeit
- AI agents
- Secrets management
- Claude Code
- Pre-commit scanning
- CI security

Das Wichtigste in Kürze
- Ein Agent kann alles lesen, was im Projektordner liegt, und Lesezugriffe im Arbeitsverzeichnis brauchen keine Freigabe. Verweigere Dateien, die nie in den Kontext sollen, mit Read-Regeln wie Read(.env).
- Berechtigungsregeln decken die Datei-Werkzeuge und gängige Dateibefehle ab, nicht jeden Prozess. Schalte die Sandbox für Shell-Befehle ein und nutze sandbox.credentials, um Zugangsdateien und Token zu sperren.
- Lokale Hooks lassen sich umgehen, also kombiniere Pre-Commit-Scans mit dem Push-Schutz von GitHub. Die serverseitige Prüfung ist die, die ein lokaler Hook nicht umgehen kann.
- Nutze in CI OIDC für den Cloud-Zugriff und gib jedem Job nur die nötigen Rechte. Wer Schreibrechte hat, kann alle Repository-Secrets lesen, also halte langlebige Schlüssel daraus fern.
- Behandle ein Secret, das in ein Transkript, ein Log oder einen Commit gelangt ist, als kompromittiert. Rotiere es zuerst und bereinige dann die Historie, denn Klone und Cache-Ansichten behalten alte Commits.
Ein Coding-Agent liest dein Projekt, führt deine Shell aus und schreibt deine Commits. Deshalb ist jedes Secret im Projektordner nur einen Tool-Aufruf vom Kontextfenster des Modells entfernt. Eine einzelne Einstellung löst das nicht. Verweigere die Lesezugriffe, die du nicht willst, setze eine Sandbox für die Shell ein, prüfe vor dem Commit, lass den Server Pushes mit Secrets ablehnen und gib CI kurzlebige Token. Mehrere dieser Kontrollen sind standardmäßig ausgeschaltet.
Fünf Wege, auf denen Secrets über einen Agenten abfließen
Die meisten dieser Lecks entstehen durch Voreinstellungen, nicht durch einen Angreifer. Genau deshalb fallen sie so leicht nicht auf.
- Die Projektdateien. Lesezugriffe im Arbeitsverzeichnis brauchen laut der Berechtigungstabelle von Claude Code keine Freigabe. Eine
.env-Datei im Projekt ist also ohne Nachfrage lesbar und landet dann in der Konversation. - Die Shell. Befehle wie
envoderprintenvgeben Werte in die Tool-Ausgabe aus, und die Sandbox von Claude Code reicht die Umgebung standardmäßig an Befehle weiter, Secrets inklusive. - Das Transkript. Claude Code speichert Sitzungstranskripte standardmäßig 30 Tage im Klartext unter
~/.claude/projects/, also bleibt alles auf der Festplatte, was der Agent ausgegeben hat. - Der Commit.
git add -Abringt den Index in Einklang mit dem Arbeitsverzeichnis, indem Einträge hinzugefügt, geändert und entfernt werden. Ignorierte Dateien werden übersprungen, also wird eine.envohne Eintrag in.gitignorein dem Moment gestaged, in dem ein Agent den Befehl ausführt. Ein Agent, der einen fehlschlagenden Test reparieren soll, kann außerdem einen Konfigurationswert in eine Fixture kopieren und committen. - Die Werkzeuge. Ein MCP-Server kann alles, was sein Token erlaubt, und die Sicherheitsempfehlungen zu MCP verlangen, dass Clients davor warnen, dass lokale Server mit denselben Rechten laufen wie der Client.
Das Kontextfenster ist der am wenigsten sichtbare Weg. Eine Datei oder Befehlsausgabe wird Teil der Konversation, und die Konversation geht bei jeder Anfrage an das Modell. Laut der Datennutzungs-Dokumentation von Claude Code gehen Prompts und Modellausgaben über TLS an die Modell-API. TLS schützt diese Verbindung, aber nicht das, was dem Modell gezeigt wird. Ein Secret lässt sich deshalb nur stoppen, bevor es gelesen wird.
Jeder Weg braucht eine eigene Kontrolle, und eine Zeile im Systemprompt gehört nicht dazu. OWASP weist darauf hin, dass Prompt Injection Anweisungen umgehen kann, etwa die Regel, niemals Secrets auszugeben. Beschränke deshalb den Zugriff auf sensible Daten nach dem Prinzip der minimalen Rechte.
Lesezugriffe zuerst verweigern: Berechtigungsregeln und Sandbox
Beginne bei den Datei-Werkzeugen. Eine Read-Deny-Regel verhindert, dass sie einen Pfad öffnen. Die Syntax folgt den gitignore-Regeln, sodass .env auf jeder Tiefe unterhalb des Arbeitsverzeichnisses passt, und ein Pfad, der mit ~/ beginnt, ist an deinen Home-Ordner gebunden. Regeln werden in der Reihenfolge deny, ask, allow geprüft, also schlägt eine Deny-Regel immer eine Allow-Regel.
{
"permissions": {
"deny": [
"Read(.env)",
"Read(secrets/**)",
"Read(~/.aws/**)",
"Read(~/.ssh/**)"
]
}
}Verwende .claude/settings.json, um eine Regel mit dem Team zu teilen, oder ~/.claude/settings.json für deinen eigenen Rechner. Deny-Regeln aus allen Ebenen werden vor Allow-Regeln ausgewertet. In den Benutzereinstellungen erreichst du mit einem ~/- oder //-Pfad jedes Projekt, weil ein führender Schrägstrich relativ zur Einstellungsdatei gilt.
Zwei Grenzen sind wichtig. Die Regeln gelten für das Read-Werkzeug, für Dateibefehle, die der Agent über Bash ausführt, etwa cat, head, tail, sed und tee, sowie für Bash-Umleitungen. Sie gelten nicht für einen Befehl, der Dateien liest, ohne sie beim Namen zu nennen, etwa grep -r pattern ., oder für ein Python- oder Node-Skript, das Dateien selbst öffnet. Und eine .claudeignore-Datei hat keine Wirkung, also verschiebe ihre Einträge in Read-Deny-Regeln.
Die Sandbox ist die zweite Schicht und standardmäßig ausgeschaltet. Schalte sie mit /sandbox ein oder setze sandbox.enabled auf true. Sie nutzt Seatbelt unter macOS und bubblewrap unter Linux und WSL2 und deckt nur Shell-Befehle ab: Datei-Werkzeuge, MCP-Server und Hooks laufen außerhalb. Die Standardeinstellungen sind weit gefasst. Lesend erreichbar ist der größte Teil des Rechners, einschließlich ~/.ssh und ~/.aws/credentials, und Umgebungsvariablen werden übernommen. Der credentials-Block schließt diese Lücken.
{
"sandbox": {
"enabled": true,
"credentials": {
"files": [
{ "path": "~/.aws/credentials", "mode": "deny" },
{ "path": "~/.ssh", "mode": "deny" }
],
"envVars": [
{ "name": "GITHUB_TOKEN", "mode": "deny" },
{ "name": "NPM_TOKEN", "mode": "deny" }
]
}
}
}Das ist das Beispiel aus der Sandbox-Dokumentation von Claude Code. Es sperrt das Lesen der AWS-Zugangsdatei und des SSH-Verzeichnisses und entfernt GITHUB_TOKEN und NPM_TOKEN aus der Umgebung sandboxierter Befehle. Lege es in ~/.claude/settings.json ab – Projekteinstellungen können die Dateisystem-Isolation nicht ausschalten. Ein PreToolUse-Hook, der mit Code 2 beendet wird, blockiert einen Aufruf, bevor die Berechtigungsregeln greifen. Hooks laufen außerhalb der Sandbox, also behandle ihre Skripte als vertrauenswürdigen Code.
Codex hat dieselbe Lücke in anderer Form, und ich habe das CLI im Codex-Review behandelt. Sein sandbox_mode kann read-only, workspace-write oder danger-full-access sein, und der Netzwerkzugriff bleibt aus, solange du ihn nicht aktivierst. Seine Shell-Umgebung behält Variablen mit KEY, SECRET oder TOKEN im Namen, weil ignore_default_excludes standardmäßig true ist. Setze den Wert auf false, damit diese Variablen vor deinen eigenen Filtern entfernt werden:
[shell_environment_policy]
inherit = "core"
ignore_default_excludes = false
[shell_environment_policy.filters]
"AWS_*" = "exclude"Transkripte, Feedback und CI-Logs
Transkripte werden am ehesten vergessen. Claude Code speichert sie standardmäßig 30 Tage im Klartext unter ~/.claude/projects/, und mit cleanupPeriodDays änderst du diesen Zeitraum. Kommerzielle Konten haben dieselbe Standardaufbewahrung von 30 Tagen, und Zero Data Retention ist für qualifizierte Enterprise-Konten verfügbar. Die Befehle /feedback, /bug und /share senden eine Kopie deines Gesprächsverlaufs, Code inklusive, an Anthropic, und diese Berichte werden fünf Jahre aufbewahrt. Personenbezogene Daten in einem Transkript bleiben personenbezogene Daten, wo immer sie liegen, also muss deine Löschroutine sie erfassen.
Die Fehlerberichte von Claude Code schwärzen bekannte Secret-Muster, aber sie betreffen nur die internen Fehler des Tools, nicht deine Prompts, Dateien oder dein Transkript. CI-Logs folgen eigenen Regeln. GitHub maskiert einen Wert nur, wenn der Runner ihn kennt, und die Maskierung beruht vor allem auf exakten Übereinstimmungen. Ein Secret, das in JSON verpackt ist, kann deshalb durchrutschen. Lege für jeden Wert ein eigenes Secret an und registriere jeden abgeleiteten Wert, etwa eine signierte oder kodierte Version.
Vor dem Commit prüfen
Drei Open-Source-Scanner decken das meiste ab, was ich einsetzen würde. Sie unterscheiden sich darin, wie sie entscheiden, dass etwas ein Secret ist, und das ist wichtiger als die Feature-Liste.
| Werkzeug | Funktionsweise | Stärke | Worauf achten |
|---|---|---|---|
| gitleaks | Erkennt Passwörter, API-Schlüssel und Token in Git-Repositories. Ein Pre-Commit-Hook prüft jeden Commit, und gitleaks git prüft die Historie. | Ein Werkzeug für Hook und Historienprüfung. | Ein lokaler Hook lässt sich mit SKIP=gitleaks umgehen. |
| detect-secrets | Prüft gegen eine Baseline-Datei. Die Baseline erfasst bekannte Secrets, und spätere Scans melden nur neue. | Die Einführung in ein Repository, das bereits Secrets enthält. | Die Baseline akzeptiert bestehende Secrets, also prüfe sie, bevor du sie committest. |
| TruffleHog | Findet mögliche Zugangsdaten und kann sie prüfen, indem es sie gegen die API des Dienstes testet, für über 800 Secret-Typen. | Aktive von toten Zugangsdaten zu trennen. | Die Prüfung schickt jeden Kandidaten an den Anbieter, also kläre, ob das zu deinen Daten passt. |
Ich würde mit gitleaks anfangen, weil sein Hook aus wenigen Zeilen YAML besteht und es auch die Historie prüft. Die Release-Seite markiert v8.30.1 als neueste Version, deshalb pinnt die Konfiguration dieses Tag. Für ein Repository, das bereits Secrets enthält, lies meine detect-secrets-Review zu Baselines.
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.30.1
hooks:
- id: gitleaksFühre pre-commit install einmal pro Klon aus. Die README von gitleaks dokumentiert auch den Notausgang SKIP=gitleaks git commit. Ein lokaler Hook fängt ehrliche Fehler ab, und der Server braucht deshalb eine eigene Prüfung.
Push-Schutz: die Prüfung, die ein lokaler Hook nicht umgeht
Der Push-Schutz von GitHub blockiert erkannte Secrets in Pushes über die Kommandozeile, in Commits, die in der GitHub-Oberfläche entstehen, in Datei-Uploads, in REST-API-Anfragen und in Interaktionen mit dem GitHub-MCP-Server, den die Dokumentation auf öffentliche Repositories beschränkt. Ein Agent kann das Remote über mehrere dieser Wege erreichen, und der Server sieht sie alle.
Zwei Details bestimmen, wie viel er dich schützt. Der Push-Schutz für Benutzer ist standardmäßig aktiv, aber nur für öffentliche Repositories auf GitHub.com. Der Push-Schutz für Repositories erfordert GitHub Secret Protection und ist ausgeschaltet, bis ein Administrator ihn aktiviert. Wer Schreibrechte hat, kann eine Sperre mit einer Begründung umgehen, und jede Umgehung erzeugt einen Alarm und einen Audit-Log-Eintrag. Die delegierte Umgehung legt fest, wer das darf.
CI: kurzlebige Token statt gespeicherter Schlüssel
Das billigste Secret zum Abfließen ist eines, das nie gespeichert wird. Mit OIDC fragt ein Workflow GitHub nach einem Token für einen einzelnen Job, und der Cloud-Anbieter prüft dessen Subject und andere Claims gegen das Vertrauen, das du eingerichtet hast. Das ausgestellte Zugriffstoken gilt nur für diesen Job. Der OWASP-Leitfaden zu Secrets empfiehlt dasselbe: kurzlebige oder dynamische Secrets bevorzugen. Der Workflow braucht eine einzige Berechtigung, um das Token anzufordern:
permissions:
id-token: write # This is required for requesting the JWT
contents: read # This is required for actions/checkoutDie GitHub-Referenz sagt, dass id-token: write dem Job nur erlaubt, das OIDC-Token abzurufen, und keinen Schreibzugriff auf andere Ressourcen gewährt. Behalte contents: read bei, solange der Job nicht pusht. Secrets werden nicht an Workflows weitergegeben, die aus einem Fork ausgelöst werden, außer GITHUB_TOKEN. Das eigentliche Risiko liegt bei pull_request_target und workflow_run: Der Leitfaden zur Härtung warnt, dass sie in Verbindung mit einem Checkout von nicht vertrauenswürdigem Pull-Request-Code diesem Code Schreibzugriff und Secrets geben können und dass dies ausgenutzt werden kann, um ein Repository zu übernehmen.
Agenten in CI verdienen dasselbe Misstrauen. Ein Agent, der ein Issue oder einen Review-Kommentar liest, liest Text, den möglicherweise jemand anderes geschrieben hat, also sollte er keine Secrets halten, die er nicht braucht. GitHub sagt, dass jeder mit Schreibrechten alle Repository-Secrets lesen kann. Environment-Secrets können hinter erforderlichen Prüfern liegen. Zur Sandbox-Seite des Problems siehe meine Checkliste zur Agenten-Sandbox.
MCP-Server: enge Token und nur Lesezugriff
Ein MCP-Server hält eine Zugangsdaten und stellt dem Agenten seine Werkzeuge bereit, daher ist sein Token die eigentliche Grenze. Die Sicherheitsempfehlungen zu MCP verbieten Token Passthrough, also dass ein Server Token akzeptiert, die nicht ausdrücklich für ihn ausgestellt wurden, und sie verlangen ein Scope-Modell nach dem Prinzip minimaler Rechte. Außerdem nennen sie Log-Lecks als einen Weg, auf dem ein Angreifer ein weit gefasstes Token erlangt. Die Identitätsseite dazu steht in KI-Agenten sind Identitäten.
Auf Seite von Claude Code unterstützt .mcp.json die Erweiterung ${VAR}, die die Dokumentation für sensible Werte wie API-Schlüssel empfiehlt, sodass die Datei im Repository einen Verweis statt des Secrets enthält. Die Dokumentation schlägt außerdem einen schreibgeschützten Datenbankbenutzer vor, damit die Abfragen von Claude keine Daten ändern können. Um alle MCP-Werkzeuge in einem Projekt abzuschalten, entfernt eine Deny-Regel mcp__* sie aus dem Kontext. Der Netzwerk-Proxy von Codex filtert laut seiner Dokumentation keine Verbindungen zu MCP-Servern.
Nach einem Leck: zuerst rotieren, dann aufräumen
Die Historie umzuschreiben ist Aufräumen, nicht die Lösung. GitHub erklärt, dass Commits nach einem Rewrite und Force-Push noch über Klone und Forks erreichbar sein können, über SHA-1-Hashes in Cache-Ansichten und über Pull Requests, die auf sie verweisen. Außerdem ändert ein Rewrite den Hash jedes späteren Commits, und ein Kollege, der einen alten Klon pusht, kann das Secret zurückbringen. Rotiere zuerst.
- Widerrufe oder rotiere die Zugangsdaten beim Anbieter, bevor du etwas anderes tust.
- Prüfe das Audit-Log des Anbieters auf Nutzung seit dem Leck, und behandle jede Nutzung, die du nicht erklären kannst, als Vorfall.
- Lösche das CI-Run-Log, das den Wert ausgegeben hat, wie GitHub empfiehlt, und jedes Transkript, das ihn enthält.
- Wenn das Repository den Wert nicht behalten darf, schreibe die Historie mit git-filter-repo um und bitte dann jeden Klon-Besitzer, neu zu klonen.
- Nimm das Muster in deinen Pre-Commit-Scanner und den Push-Schutz auf, damit das nächste Leck abgelehnt wird.
Checkliste
| Kontrolle | Wo sie liegt | Was sie verhindert | So prüfst du sie |
|---|---|---|---|
| Read-Deny-Regeln | permissions.deny in settings.json | Datei-Werkzeuge des Agenten sowie cat, head oder tail, die Secrets lesen | Lass eine Testsitzung .env lesen und erwarte eine Sperre |
| Sandbox mit gesperrten Zugangsdaten | sandbox.enabled und sandbox.credentials | Lesende Shell-Zugriffe auf ~/.aws und ~/.ssh sowie übernommene Token | Führe /sandbox aus und prüfe, dass sie aktiv ist |
| Codex-Umgebungsfilter | shell_environment_policy in config.toml | KEY-, SECRET- und TOKEN-Variablen, die Befehle erreichen | Prüfe, dass ignore_default_excludes auf false steht |
| Pre-Commit-Scan | gitleaks-Hook in .pre-commit-config.yaml | Secrets in lokalen Commits | Committe einen Fake-Schlüssel auf einem Branch und erwarte eine Ablehnung |
| Push-Schutz | Sicherheitseinstellungen des Repositorys | Secrets in Pushes, UI-Commits, Uploads und MCP-Aufrufen | Prüfe, dass er aktiv ist und die Umgehung eingeschränkt ist |
| OIDC für den Cloud-Zugriff | id-token: write und die Vertrauensrichtlinie der Cloud | Langlebige Cloud-Schlüssel in Repository-Secrets | Lösche die Cloud-Schlüssel, sobald das Vertrauen funktioniert |
| Token mit minimalen Rechten | permissions-Block in jedem Workflow | Missbrauch von GITHUB_TOKEN | Standardmäßig contents: read setzen |
| Log-Hygiene | Abgeleitete Werte registriert, keine in JSON verpackten Secrets | Secrets in CI-Logs | Durchsuche ein Run-Log nach einem Testwert |
| Rotations-Runbook | Konsole des Anbieters und Audit-Logs | Gültige Werte nach einem Leck | Probe es an einem Schlüssel mit geringem Wert |
Was ich zuerst tun würde
- Lege Read-Deny-Regeln für .env, Secret-Ordner, ~/.aws und ~/.ssh an. Lösche jede .claudeignore, auf die du dich verlassen hast.
- Schalte die Sandbox ein und ergänze credentials-Einträge für die Variablen und Dateien, die du tatsächlich nutzt.
- Installiere gitleaks als Pre-Commit-Hook und aktiviere den Push-Schutz für jedes Repository, das ein Agent erreichen kann.
- Stelle den Cloud-Zugriff in CI auf OIDC um und setze die Berechtigungen jedes Workflows auf das Minimum.
- Schreibe das Rotations-Runbook, bevor du es brauchst, und probiere es an einem Schlüssel mit geringem Wert aus.
Dafür brauchst du keine Plattform – du musst die Voreinstellungen ändern, ein paar Konfigurationsdateien ins Repository legen und dir bei jedem Secret die Frage stellen, ob der Agent es lesen, ausgeben oder committen könnte.
Quellen
- Claude Code: Berechtigungen
- Claude Code: Sandbox für Bash
- Claude Code: Hooks
- Claude Code: Datennutzung
- Claude Code: Einstellungen
- Claude Code: MCP-Server
- Codex: Konfigurationsreferenz
- Codex: Agent-Freigaben und Sicherheit
- Codex: erweiterte Konfiguration
- GitHub: Push-Schutz
- GitHub: Sicherheitshärtung mit OIDC
- GitHub: OpenID-Connect-Referenz
- GitHub: Secrets in Actions verwenden
- GitHub: Referenz zur sicheren Nutzung
- GitHub: sensible Daten aus einem Repository entfernen
- gitleaks: README und neueste Version
- TruffleHog: README
- detect-secrets: README und neueste Version
- Model Context Protocol: Sicherheitsempfehlungen
- OWASP: Secrets Management Cheat Sheet
- OWASP: LLM02 Offenlegung sensibler Informationen
- Git: Dokumentation zu git-add
Häufige Fragen
Kann ich verhindern, dass Claude Code meine .env-Datei liest?
Ja. Eine Read-Deny-Regel wie Read(.env) in permissions.deny sperrt die Datei-Werkzeuge, und dieselbe Regel gilt auch für Dateibefehle wie cat, head und tail, die über Bash laufen. Ein Skript, das Dateien selbst öffnet, stoppt sie nicht, deshalb schalte zusätzlich die Sandbox ein.
Hält eine .claudeignore-Datei Secrets von Claude Code fern?
Nein. Laut der Dokumentation zu den Berechtigungen hat eine .claudeignore-Datei keine Wirkung. Verschiebe ihre Einträge deshalb in Read-Deny-Regeln.
Werden die Secrets, die ein Agent liest, an das Modell gesendet?
Was der Agent liest, wird Teil der Konversation, und die Konversation geht bei jeder Anfrage an die Modell-API. Laut der Datenschutz-Dokumentation von Anthropic werden Prompts und Modellausgaben über TLS übertragen, und Sitzungstranskripte bleiben standardmäßig 30 Tage lokal im Klartext gespeichert.
Was tue ich, wenn ein Schlüssel in einem Commit landet?
Rotiere den Schlüssel zuerst. Die Historie umzuschreiben reicht nicht, weil Klone, Forks, Cache-Ansichten und Pull Requests den Commit weiterhin enthalten können. GitHub Support entfernt sensible Daten nur dort, wo das Rotieren der Zugangsdaten das Risiko nicht mindern kann.