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.

··11 Min. Lesezeit

  • AI agents
  • Secrets management
  • Claude Code
  • Pre-commit scanning
  • CI security
Titelbild zu Coding-Agenten und Secrets: ein Schild aus sechs gestaffelten Kontrollen, von Deny-Regeln und Sandbox bis zur Rotation.

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.

Diesen Artikel anhören

0:000:00

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 env oder printenv geben 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 -A bringt den Index in Einklang mit dem Arbeitsverzeichnis, indem Einträge hinzugefügt, geändert und entfernt werden. Ignorierte Dateien werden übersprungen, also wird eine .env ohne Eintrag in .gitignore in 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.

Drei Wege, auf denen ein Secret ein Projekt verlässtDrei Reihen mit je vier Schritten. Die erste Reihe führt von einem Secret in der .env-Datei über das Read-Werkzeug und das Kontextfenster zum Modell-Anbieter. Die zweite führt von einem Shell-Befehl über seine Ausgabe in ein Klartext-Transkript und einen Feedback-Bericht. Die dritte führt von git add -A über einen Commit und einen Push zum Remote-Repository und seinen Cache-Ansichten. Jede Reihe braucht eine eigene Kontrolle.Secret in .envProjektordnerRead-Werkzeugohne NachfrageKontextfensterpro Anfrage erneutModell-Anbietererhält die DateiShell-Befehlenv oder printenvBefehlsausgabeSchlüssel offenTranskript~/.claude, 30 TageFeedback-Bericht/feedback sendetgit add -AIgnoriertes fehltCommitHistorie behält siePushServer lehnt abRemote und ForksCache-Ansichten
Drei Wege aus einem Projekt. Jeder braucht eine eigene Kontrolle, vom Deny-Regel-Schutz für den ersten bis zum Push-Schutz für den letzten.

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.

WerkzeugFunktionsweiseStärkeWorauf achten
gitleaksErkennt 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-secretsPrü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.
TruffleHogFindet 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: gitleaks

Fü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/checkout

Die 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.

  1. Widerrufe oder rotiere die Zugangsdaten beim Anbieter, bevor du etwas anderes tust.
  2. Prüfe das Audit-Log des Anbieters auf Nutzung seit dem Leck, und behandle jede Nutzung, die du nicht erklären kannst, als Vorfall.
  3. Lösche das CI-Run-Log, das den Wert ausgegeben hat, wie GitHub empfiehlt, und jedes Transkript, das ihn enthält.
  4. 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.
  5. Nimm das Muster in deinen Pre-Commit-Scanner und den Push-Schutz auf, damit das nächste Leck abgelehnt wird.

Checkliste

KontrolleWo sie liegtWas sie verhindertSo prüfst du sie
Read-Deny-Regelnpermissions.deny in settings.jsonDatei-Werkzeuge des Agenten sowie cat, head oder tail, die Secrets lesenLass eine Testsitzung .env lesen und erwarte eine Sperre
Sandbox mit gesperrten Zugangsdatensandbox.enabled und sandbox.credentialsLesende Shell-Zugriffe auf ~/.aws und ~/.ssh sowie übernommene TokenFühre /sandbox aus und prüfe, dass sie aktiv ist
Codex-Umgebungsfiltershell_environment_policy in config.tomlKEY-, SECRET- und TOKEN-Variablen, die Befehle erreichenPrüfe, dass ignore_default_excludes auf false steht
Pre-Commit-Scangitleaks-Hook in .pre-commit-config.yamlSecrets in lokalen CommitsCommitte einen Fake-Schlüssel auf einem Branch und erwarte eine Ablehnung
Push-SchutzSicherheitseinstellungen des RepositorysSecrets in Pushes, UI-Commits, Uploads und MCP-AufrufenPrüfe, dass er aktiv ist und die Umgehung eingeschränkt ist
OIDC für den Cloud-Zugriffid-token: write und die Vertrauensrichtlinie der CloudLanglebige Cloud-Schlüssel in Repository-SecretsLösche die Cloud-Schlüssel, sobald das Vertrauen funktioniert
Token mit minimalen Rechtenpermissions-Block in jedem WorkflowMissbrauch von GITHUB_TOKENStandardmäßig contents: read setzen
Log-HygieneAbgeleitete Werte registriert, keine in JSON verpackten SecretsSecrets in CI-LogsDurchsuche ein Run-Log nach einem Testwert
Rotations-RunbookKonsole des Anbieters und Audit-LogsGültige Werte nach einem LeckProbe es an einem Schlüssel mit geringem Wert

Was ich zuerst tun würde

  1. Lege Read-Deny-Regeln für .env, Secret-Ordner, ~/.aws und ~/.ssh an. Lösche jede .claudeignore, auf die du dich verlassen hast.
  2. Schalte die Sandbox ein und ergänze credentials-Einträge für die Variablen und Dateien, die du tatsächlich nutzt.
  3. Installiere gitleaks als Pre-Commit-Hook und aktiviere den Push-Schutz für jedes Repository, das ein Agent erreichen kann.
  4. Stelle den Cloud-Zugriff in CI auf OIDC um und setze die Berechtigungen jedes Workflows auf das Minimum.
  5. 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

  1. Claude Code: Berechtigungen
  2. Claude Code: Sandbox für Bash
  3. Claude Code: Hooks
  4. Claude Code: Datennutzung
  5. Claude Code: Einstellungen
  6. Claude Code: MCP-Server
  7. Codex: Konfigurationsreferenz
  8. Codex: Agent-Freigaben und Sicherheit
  9. Codex: erweiterte Konfiguration
  10. GitHub: Push-Schutz
  11. GitHub: Sicherheitshärtung mit OIDC
  12. GitHub: OpenID-Connect-Referenz
  13. GitHub: Secrets in Actions verwenden
  14. GitHub: Referenz zur sicheren Nutzung
  15. GitHub: sensible Daten aus einem Repository entfernen
  16. gitleaks: README und neueste Version
  17. TruffleHog: README
  18. detect-secrets: README und neueste Version
  19. Model Context Protocol: Sicherheitsempfehlungen
  20. OWASP: Secrets Management Cheat Sheet
  21. OWASP: LLM02 Offenlegung sensibler Informationen
  22. 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.

Klingt nach dem, was du suchst?

Erzähl mir von deinem Projekt oder deiner Stelle – ich freue mich, von dir zu hören.