Du arbeitest mit einem KI-Coding-Agent — Claude Code, Cursor, egal — in deinem Repo, und für „mach den Pull Request auf, ruf die Staging-API, stoß das Deployment an" braucht er Zugangsdaten. Gibst du ihm welche, ist die naheliegende Übergabe schon der Leak — und „dann gebe ich ihm eben keine" rettet dich nicht: dann besorgt er sie sich selbst, liest die .env, fragt den Vault — und leakt sie dabei, ohne dass du es überhaupt merkst. In beiden Fällen wandert der Wert durch das KI-Modell und ist damit weg.
Der unangenehme Teil: in den meisten Agent-Setups verlässt das Credential deine Infrastruktur und landet beim Modell-Anbieter oder einem von ihm betriebenen Dienst — gespeichert, aus deiner Kontrolle. (Self-hosted- oder Enterprise-Modelle ändern Speicherung und Training — aus der Hand ist der Wert trotzdem.) Für die meisten Unternehmen ist das zugleich ein Compliance-Problem. Hier ist, warum das passiert und wie man es dauerhaft schließt.
Kurz gesagt — KI-Agents leaken Zugangsdaten, indem sie sie als Output lesen, der an den Modell-Anbieter geht. Die Lösung: einen Agent ein Secret benutzen lassen, ohne dass er es je sieht.
Warum das naheliegende Setup leakt
Der Mechanismus ist simpel — und tückisch: in dem Moment, in dem der Agent den Wert liest — aus .env, Vault oder deiner Nachricht — wird er Teil der Konversation, die der Agent an den KI-Anbieter schickt. Der Token steht jetzt in fremden Logs. Den Chat hinterher zu löschen bringt nichts; übertragen wurde er in dem Moment, in dem er auftauchte.
Mit „bitte sei vorsichtig" im Prompt löst du das nicht. Eine Anweisung im Prompt ist eine Empfehlung, und Empfehlungen werden ignoriert — von diesem Agent, vom nächsten oder von irgendeinem Tool drumherum. Die Lösung muss strukturell sein: das Secret muss unmöglich zu sehen sein, nicht bloß unerwünscht.
Das ist nicht hypothetisch. Anfang 2026 fanden Sicherheitsforscher heraus, dass 7 % der Skills auf ClawHub — dem Marktplatz für Erweiterungen der KI-Agent-Plattform OpenClaw — Zugangsdaten auf genau diese Weise leakten; den Vorfall habe ich in dem OpenClaw-Leak aufgedröselt. Das ist kein OpenClaw-Spezialfall: dieselbe Mechanik trifft jede Skill, jedes MCP-Tool und jeden Befehl, den Claude Code oder Cursor ausführt — liest die Erweiterung ein Secret, steht es im Modell-Kontext.
Und es sind nicht nur Agents, die sich Secrets holen: Millionen Entwickler — und „Vibe-Coder" — pasten API-Keys jeden Tag direkt in den Prompt. Dasselbe Leck, nur von Hand — aus Bequemlichkeit und in dem leichtgläubigen Vertrauen, dass schon nichts passiert.
Benutzen ist nicht Sehen
Die ganze Lösung in einem Satz: ein Agent muss ein Credential benutzen, aber er muss es nie sehen. Das sind zwei verschiedene Dinge, und sie auseinanderzuhalten ist der ganze Trick.
Konkret: das Secret geht in das Programm, das der Agent ausführt — nie in den Text, den das KI-Modell liest. Die Shell sitzt dazwischen: sie reicht den Wert als Umgebungsvariable an ein Programm weiter, während der Agent nur den Befehl sieht, den er getippt hat.
Eine Abgrenzung vorweg: Es geht hier ums versehentliche Leaken im Alltag — das Modell bekommt den Wert nie zu sehen. Ein vollständig kompromittierter Agent mit freier Shell-Ausführung bleibt ein anderes Bedrohungsmodell; dazu unten mehr.
Wo wohnt das Secret?
Ein Secret aus dem Blickfeld der KI zu halten, funktioniert nur, wenn es irgendwo liegt, wo ein Tool es bei Bedarf holen kann — nicht hartkodiert im Repo, nicht in eine Config gepastet, die der Agent liest. Dieses „irgendwo" ist ein Secrets-Manager (ein Tresor): ein Ort, der Zugangsdaten verschlüsselt speichert, sie an autorisierte Programme aushändigt und protokolliert, wer was angefasst hat.
Ich nutze Infisical — Open Source und self-hostbar (die Secrets bleiben auf meiner Infrastruktur), mit vollwertigen Machine Identities: headless-Auth über kurzlebige Tokens, gemacht für genau diese Agent- und CI-Nutzung. Der eigentliche Gewinn für dieses Problem: Infisical erledigt drei Dinge in einem — das Secret holen, in die Umgebung des Programms legen und den Befehl ausführen — und der Wert erscheint dabei nie auf stdout. Genau diese Kette (holen → Env → ausführen) nimmt dir ein Tresor wie HashiCorp Vault, pass oder AWS Secrets Manager nicht ab: die geben nur den Wert zurück, die Kette musst du selbst mittels eines Wrapper-Skripts verdrahten. Beides führt zum selben Ergebnis; Infisical ist nur der Weg des geringsten Widerstands.
In der Praxis: meistens ein einziger Befehl
Sobald das Secret in Infisical liegt, sieht das in der Praxis so aus: Der Agent soll deine offenen Pull Requests auflisten — dafür ruft er GitHubs CLI gh pr list auf, und gh braucht dazu deinen GitHub-Token. Statt ihm den Token in die Hand zu drücken, lässt du Infisical den Befehl starten:
infisical run --env=prod --path=/git -- gh pr list
Schritt für Schritt: infisical run holt die Secrets aus dem Ordner /git (Umgebung prod) und startet damit einen neuen Prozess für den Befehl nach dem -- (hier gh pr list) — mit den Secrets in dessen Umgebung. Dabei wird jedes Secret zu einer Umgebungsvariable mit demselben Namen wie sein Schlüssel in Infisical: im /git-Ordner liegt ein Secret GITHUB_TOKEN, also steht im Prozess $GITHUB_TOKEN. Das GitHub-CLI gh liest diese Variable von sich aus — so wie viele Tools ihren Token aus einer bekannten Env-Variable ziehen — und erledigt seine Arbeit. Der entscheidende Punkt: das Secret lebt in diesem Kind-Prozess, nicht beim Agent, und sein Wert taucht nie auf stdout auf — also nicht im Transcript, das der Agent an die KI schickt. Das ist die „holen → in die Env → ausführen"-Kette von oben, fertig in einem Befehl. Für die meisten Teams ist das bereits die Lösung.

/git liegt ein Secret namens GITHUB_TOKEN — genau dieser Schlüssel wird im Prozess zu $GITHUB_TOKEN (die Werte bleiben maskiert).Ich packe ihn in einen get-secret-Helper, damit der Aufruf kurz ist und die Auth automatisch passiert:
get-secret exec git -- gh pr list
Darunter ist das nur infisical run mit einem Machine-Identity-Token aus dem OS-Keychain — so spart sich jeder Aufruf das manuelle Setzen von --token, --projectId und --domain. Zum Benutzen eines Secrets könntest du genauso das nackte infisical run tippen; hier ist der Wrapper reine Bequemlichkeit.
Damit ist das Leck geschlossen: das Secret wird benutzt, aber nie gesehen — weder vom Modell noch im Transcript. Ich hab's adversarial geprüft: echten Wert holen, alles einfangen, was der Befehl ausgibt, die Ausgabe nach dem Secret durchsuchen — über get-secret exec taucht er nirgends auf, wo ein Agent ihn lesen könnte.
Einzelne Werte lesen (mit Guardrail)
Das deckt das Benutzen ab. Manchmal willst du einen Wert aber nur anschauen (debuggen, schnell prüfen) — oder du bittest den Agent direkt darum, etwa »zeig mir das DB-Passwort für staging«. Dafür gibt es einen Lese-Pfad, und der Haken: infisical secrets get GITHUB_TOKEN druckt den Wert im Klartext nach stdout. Für einen Menschen am Terminal ok, für einen Agent ist dieser stdout wieder das Transcript.
Also setzen wir einen kleinen Guardrail davor: den Wert nur ausgeben, wenn wirklich ein Mensch zuschaut.
# get-secret <folder>/<NAME> — gibt einen Wert aus, aber nur an einen Menschen
val=$(infisical secrets get "$name" --env=prod --path="/$folder" --plain --silent)
# echtes Terminal -> ein Mensch; eine Pipe -> ein Agent
if [ -t 1 ]; then
printf '%s\n' "$val"
else
printf '[redacted len=%s]\n' "${#val}"
echo "im Terminal anzeigen: get-secret $folder/$name --show" >&2
fi
[ -t 1 ] prüft, ob stdout ein echtes Terminal ist. Der stdout eines Agents ist im Normalfall eine Pipe — er bekommt also [redacted len=40] statt des Werts, plus den Hinweis, wie ein Mensch ihn am Terminal vollständig anzeigt (--show).
Damit kein Missverständnis entsteht: Das ist ein Guardrail, kein Schutzwall — und nicht die eigentliche Verhinderung. Die steckt in exec; den rohen Lese-Pfad braucht ein Agent zum Benutzen gar nicht. Der Guardrail fängt nur den häufigsten Reflex-Fehler ab — „Wert holen und ausgeben" — und sitzt allein auf diesem Wrapper. Wer Shell-Zugriff und den Token hat, ruft die rohe CLI direkt auf und kommt an den Wert (oder allokiert ein Pseudo-Terminal, dann ist [ -t 1 ] wahr). Eine echte Wand bekommst du nur, indem du dem Agent den Token gar nicht erst gibst — etwa in einer Sandbox, die nur den gegateten Wrapper kennt, nicht die rohe CLI.
Woher weiß der Agent, dass er exec nutzt?
Von allein gar nicht — du sagst es ihm einmal. In die Anweisungen des Agents — CLAUDE.md, AGENTS.md, ein Skill oder der System-Prompt — schreibst du: „Secrets über get-secret exec benutzen, nie den Rohwert abfragen."
Moment: ist das nicht wieder bloß eine Prompt-Regel, von denen ich oben sagte, sie taugen nicht als Grenze? Doch — aber diese hat ein Sicherheitsnetz. Befolgt der Agent sie, läuft alles leakfrei über exec. Greift er stattdessen zum Lese-Pfad, bekommt er [redacted]. Die Regel wählt nur den bequemen Weg; dass der naheliegende Fehler nichts preisgibt, sorgt die Struktur (Env-Injektion + Gate). Genau das ist der Unterschied zwischen Verhalten erbitten und Verhalten absichern.
Wenn dein Tresor kein run mitbringt
infisical run nimmt dir den Injection-Schritt ab: Secrets holen, in die Umgebung legen, Kommando ausführen, nie stdout berühren. Tresore wie HashiCorp Vault, pass oder AWS Secrets Manager geben dir nur den Wert zurück — diesen einen Schritt baust du also selbst. Es sind eine Handvoll Zeilen:
#!/bin/bash
# run-with-secret <cmd...> — Wert aus beliebigem Tresor in die Env, dann Kommando ausführen.
export GITHUB_TOKEN="$(vault kv get -field=token secret/git)" # oder: pass show git/token, aws secretsmanager get-secret-value …
exec "$@"
run-with-secret gh pr list reicht den Token über Command-Substitution in die Umgebung an gh — er wandert nie über stdout, der Agent sieht nur den Befehl, nie den Wert. Mehrere Secrets? Mehrere export-Zeilen. Genau das macht get-secret exec unter der Haube; Infisical erspart dir nur, es zu schreiben.
Die Aufteilung ist also einfach. Das Read-Gate von oben baust du einmal, egal welcher Tresor. Die Injection ist mit Infisical (infisical run) gratis und mit allem anderen ein paar Zeilen. Die Sicherheit steckte nie im Tresor — sie steckt darin, wie du den Wert an den Agent übergibst: in den Prozess, nie in den Prompt.
Ein Secret schreiben
Ein neues Secret zu schreiben ist die asymmetrische Richtung: der Wert muss von irgendwoher hereinkommen, und wenn ein Mensch ihn in den Chat pastet, damit der Agent ihn speichert, ist dieser Paste schon das Leck. Der Schreibpfad nimmt Werte deshalb nur über stdin oder eine Datei, nie in einen Befehl getippt:
echo -n "$VALUE" | set-secret git/TOKEN - # wrappt `infisical secrets set`, liest stdin
Zum Nachbauen: die Scripts
Das Ganze ist am Ende ein kurzes Shell-Skript: infisical run zum Benutzen, ein TTY-gegateter infisical secrets get zum Lesen, und als Auth eine headless Machine Identity aus dem OS-Keychain (kein interaktives Login). Hier die beiden Wrapper als Download: get-secret und set-secret.
Installation (macOS; setzt die infisical-CLI und jq voraus):
mkdir -p ~/.local/bin
curl -fsSL https://th3chris.com/scripts/get-secret -o ~/.local/bin/get-secret
curl -fsSL https://th3chris.com/scripts/set-secret -o ~/.local/bin/set-secret
chmod +x ~/.local/bin/get-secret ~/.local/bin/set-secret
Die Scripts gehen von macOS aus — die Credentials liegen im Keychain. Das Muster selbst ist plattformunabhängig: auf Linux/Windows tauschst du nur den security-Aufruf (Schritt 3) gegen deinen OS-Secret-Store (libsecret/pass, Windows Credential Manager) oder gibst die Machine Identity per INFISICAL_TOKEN-Env rein.
Setup (einmalig):
-
infisical-CLI undjqinstallieren. -
Eine Machine Identity in Infisical anlegen (Org → Access Control → Identities → Universal Auth), ein Client Secret erzeugen und der Identity Lese-Rechte aufs Projekt geben (für
set-secretauch Schreiben). -
Client-ID und Secret in den Keychain legen (Service-Name = dein
KEYCHAIN_SVC), damit es nicht in der Shell-History landet:security add-generic-password -s infisical-machine-identity -a "$USER" -U -w # am Prompt eingeben: CLIENT_ID:CLIENT_SECRET -
Oben im Script den
CONFIG-Block ausfüllen — nur nochINFISICAL_DOMAIN(deine Instanz oderhttps://app.infisical.com) undKEYCHAIN_SVC(der Name aus Schritt 3). Projekt und Environment stehen nicht hier, sondern pro Repo (Schritt 5). -
Pro Repo das Projekt festlegen — eine
.infisical.jsonmitworkspaceId(Projekt-ID aus der Projekt-URL bzw. Project Settings) unddefaultEnvironment(Env-Slug, z.B.prod):printf '{"workspaceId":"<projectId>","defaultEnvironment":"prod"}' > .infisical.json(
infisical initmacht das interaktiv, braucht aber einen Login; mit reiner Machine Identity schreibst du die nicht-sensible Datei von Hand.)
Danach läuft get-secret exec <folder> -- <cmd> im Repo.
Mehrere Projekte? Du kopierst das Script nicht pro Projekt — es liegt einmal global im PATH (~/.local/bin). Welches Infisical-Projekt gilt, steht in der .infisical.json des jeweiligen Repos: der Wrapper liest daraus die workspaceId und reicht sie als --projectId weiter. Das ist nötig, weil Machine-Identity-Auth bei Infisical immer eine explizite Projekt-ID verlangt — die .infisical.json-Autoerkennung der CLI greift nur beim interaktiven Login. So trifft get-secret exec git -- … im jeweiligen Repo automatisch das richtige Projekt — ohne Flag, ohne zweite Kopie; fehlt die Datei, bricht der Wrapper mit einem Hinweis ab.
Das ist ein Workstation-Setup. In CI gibt es keinen Keychain — dort gibst du den Token über INFISICAL_TOKEN aus einer maskierten CI-Variable rein oder nutzt OIDC-Auth. Und: Universal Auth mit statischem Client Secret ist die einfachste, nicht die sicherste Variante — rotier es wie jedes Credential, oder nimm OIDC/native Identitäten, wo die Laufzeit es hergibt.
Die meisten Unternehmen lösen das heute mit Prompt-Regeln — „bitte keine Secrets ausgeben". Das Problem: eine Prompt-Regel ist keine Sicherheitsgrenze. Wer KI-Agenten produktiv einsetzen will, braucht technische Grenzen statt Verhaltensregeln — genau die Art, die dieser Artikel beschreibt. Solche Grenzen sauber zu ziehen, damit Automatisierung schnell bleibt, ohne still Risiko aufzubauen, ist Teil meiner Arbeit.
