Geheimnis-Schutz

API-Keys im Prompt schützen: so schwärzt cloak sie vor dem Modell

Warum Schlüssel und Passwörter in KI-Prompts landen, was das kostet — und wie cloak sie durch Platzhalter ersetzt, bevor die Anfrage rausgeht. Mit Belegen.

UltraNOS12 Min. Lesezeit

Ein API-Key landet nicht im Prompt, weil jemand ihn dort hineintippt. Er landet dort, weil du eine Fehlermeldung samt Umgebung kopierst, weil ein Coding-Agent read_file auf die .env ausführt, weil ein Terminal-Befehl env ausgibt oder weil ein Log die Authorization-Kopfzeile mitschneidet. Ab dem Moment ist der Prompt beim Modellanbieter ein Datensatz. Wie lange er dort liegt, regeln dessen Bedingungen — und ein Trainings-Opt-out ist keine Löschung, sondern nur die Zusage, den Datensatz nicht zum Trainieren zu benutzen. Der belastbare Weg ist deshalb nicht Aufpassen, sondern Ersetzen: Geheimnisse werden vor dem Senden durch Platzhalter ausgetauscht. Genau das macht cloak in UltraNOS, und dieser Beitrag beschreibt den Weg Schritt für Schritt, samt der Stellen, an denen er heute noch Lücken hat.

Warum „ich pass schon auf" nicht reicht

Solange du selbst jeden Prompt schreibst, kannst du Schlüssel vor dem Absenden entfernen. Ein Agent tut das nicht. Er liest Dateien, führt Befehle aus und schickt die Ergebnisse als nächste Runde an das Modell — ohne dass du sie vorher siehst. Drei konkrete Wege, alle aus dem Quellenmaterial im Repo:

  1. Werkzeugergebnisse. Im Anthropic-Draht ist tool_result.content die Stelle, an der Shell-Ausgaben, Dateiinhalte und die Ausgabe von env ankommen. Die cloak-Implementierung nennt sie in cloak/cloak/transform/wire_anthropic.py die „ergiebigste Quelle". Ein Agent, der eine Konfiguration liest, um einen Fehler zu verstehen, schickt sie vollständig ans Modell.
  2. Editor-Vervollständigung. Die Inline-Vorschläge im Editor gehen mit dem umgebenden Dateiinhalt an das Modell. In UltraNOS lief dieser Pfad bis zum 19.08.2026 ungeschützt; die Korrektur steht in backend/api/completions_routes.py. Wer eine .env im Editor offen hat, hat den Schlüssel im Kontext.
  3. Dokumentation und Repos. Beim Messlauf der Erkennungsregeln gegen das eigene Repo (Commit 27a5558) waren alle Treffer echte Geheimnisse, null Fehlalarme — und sie lagen in Projektdateien, die eine KI-Session automatisch einliest, nicht in irgendwelchen Ecken.

Die Beobachtung von Truffle Security, dass Modelle Entwickler:innen zum Hartkodieren von Schlüsseln anleiten (trufflesecurity.com/blog), passt dazu: Der Prompt ist kein Sonderfall, sondern der Normalweg, auf dem Geheimnisse den Rechner verlassen. Die OWASP Top 10 für LLM-Anwendungen führen „Sensitive Information Disclosure" aus gutem Grund als eigenen Punkt.

Was cloak macht — Schritt für Schritt

cloak ist ein Python-Paket ohne Laufzeitabhängigkeiten, das im UltraNOS-Backend im Prozess unmittelbar vor dem Senden greift (backend/privacy/cloak_gate.py). Kein Proxy dazwischen, keine zweite Verbindung: Der Anfragekörper wird gescannt, ersetzt, dann geschickt.

Erkennung

Fünf Kategorien werden ersetzt: secret, secret_block, credential, email und person (cloak/cloak/types.py). Alle sind in der Vorgabe aktiv. Die Erkennung läuft als Pipeline aus acht festen Stufen, deren Reihenfolge den Vorrang bestimmt (cloak/cloak/detect/engine.py):

Stufe Was sie tut
0 vorhandene Platzhalter erkennen, nicht doppelt ersetzen
1 Allowlist: Public-Keys, Digests, data:-URIs, Port-Mappings u. a. reservieren
2 Schlüsselblöcke (PEM, PuTTY)
3 Anbieter-Präfixe (sk-ant-…, ghp_…, AKIA… u. a.)
4 strukturelle Credentials (KEY=wert, Authorization:, Connection-Strings, JWT, .netrc, curl -u)
5 Tresor-Werte, die du selbst hinterlegt hast
6 E-Mail-Adressen
7 kontextlose Formen (mysql -p, redis-cli -a, --password, opake Tokens ab Entropie 4,0)

In Zahlen, per Import gezählt am 07.09.2026: 27 Präfixregeln, 13 strukturelle Regeln, 2 Blockregeln und 8 Allowlist-Reservierungen in cloak/cloak/detect/patterns.py, dazu 6 Regeln in detect/unbekannt.py und die E-Mail-Regel in detect/pii.py. Summe: 57 Regel-Specs, davon 49 ersetzend und 8 reservierend, plus der Tresor-Detektor für bekannte Werte.

Jede Regel ist ein regulärer Ausdruck mit begrenzten Quantoren — kein .*, als Bremse gegen ReDoS — und hat vier Tore: Mindest- und Höchstlänge, eine Ablehnungsliste und eine Entropieschwelle (detect/rules.py). Die Entropie ist Shannon-Bit je Zeichen und greift nur nach einem Kontextanker; die Schwellen liegen zwischen 2,0 für KEY=wert-Zuweisungen und 4,0 für opake Tokens. Ablehnungslisten fangen Doku-Beispiele hinter echtem Präfix, reine Platzhalter wie <token> oder $VAR, schwache Passwörter und Code-Referenzen ab. Bei überlappenden Treffern gewinnt die schützenswertere Kategorie, nicht der längere Treffer (cloak/cloak/spans.py).

Bei KEY=wert wird übrigens nur der Wert ersetzt. Das Modell sieht ANTHROPIC_API_KEY=CLOAK_SECRET_… und weiß damit weiterhin, worum es geht.

Platzhalter

Ein Platzhalter hat die Form CLOAK_<KATEGORIE>_<12 Hex>, also CLOAK_SECRET_…, CLOAK_CREDENTIAL_…, CLOAK_EMAIL_… und so weiter; der längste ist 31 Zeichen (cloak/cloak/placeholder.py). Die ID ist zufällig aus secrets.token_hex, kein Hash des Werts — aus dem Platzhalter lässt sich nichts zurückrechnen.

Trotzdem ist er stabil: Taucht derselbe Wert wieder auf, sucht der Tresor per Index-HMAC über Pepper, Kategorie und Wert den vorhandenen Eintrag und gibt denselben Platzhalter erneut aus (cloak/vault/store.py). Das gilt prozessweit und über Sitzungen hinweg. Für das Modell heißt das: Der gleiche Schlüssel heißt in Runde 1 und Runde 12 gleich, es kann ihn also konsistent in Befehlen verwenden.

Das Modell bekommt außerdem einen kurzen englischen Hinweis [cloak] Privacy layer active … mit drei Regeln — nur im Profil enforce, nur wenn der Körper überhaupt einen Platzhalter trägt (cloak/cloak/transform/notice.py).

Rück-Ersetzung bei Werkzeugaufrufen

Der eigentliche Trick liegt hinter dem Modell. Wenn es einen Werkzeugaufruf mit einem Platzhalter zurückgibt, etwa curl -H "Authorization: Bearer CLOAK_SECRET_…", löst UltraNOS den Platzhalter unmittelbar vor der Ausführung auf (sitzung.werkzeug() in cloak_gate.py). Das Werkzeug bekommt den echten Wert. Der Verlauf, der zurück ans Modell geht, behält den Platzhalter. Und das Werkzeugergebnis wird in jeder Runde erneut komplett verhüllt — auch wenn das Modell per read_file oder Bash gerade frische Geheimnisse gelesen hat, sofern ein Muster greift.

Die Rückübersetzung speist sich ausschließlich aus dem Ledger dieses Zugs. Ein Ledger je Nutzer-Zug, über alle Werkzeugrunden gehalten; ein Platzhalter aus einer anderen Sitzung wird in diesem Zug nicht aufgelöst (cloak/cloak/engine.py).

Für die Antwort an dich gilt: Der Strom wird zurückübersetzt, mit einem Rückhalt von höchstens 32 Zeichen an Chunk-Grenzen, damit ein Platzhalter nicht durch eine Chunk-Naht rutscht (cloak/cloak/streaming.py). Secrets bleiben für Menschen im Anzeigetext Platzhalter (reveal=false), E-Mail-Adressen und Personen erscheinen wieder im Klartext.

Fail-closed: was passiert, wenn der Schutz nicht bereit ist

Die Vorgabe ist die unbequeme: Ist das Feature-Flag ULTRANOS_FEATURE_CLOAK gesetzt und das Paket fehlt oder die Laufzeit öffnet nicht, wirft das Gate CloakNichtVerfuegbar, und der Zug scheitert lesbar, statt Klartext zu senden (cloak_gate.py). Kein Provider fängt das ab; auf dem CLI-Weg wird die CLI gar nicht erst gestartet (llm/test_cloak_cli.py). Die Editor-Vervollständigung liefert in dem Fall keinen Vorschlag statt einen unverhüllten.

Das ist die richtige Vorgabe, weil die Alternative — leise durchreichen — genau den Zustand herstellt, den es am 19.08.2026 früh gab: Ein Vorlauf des Pakets hatte audit als Vorgabeprofil, zählte Treffer und reichte den Text byte-identisch mit HTTP 200 weiter. Das Changelog im Repo nennt das „schlechter als kein Proxy", weil es Schutz suggeriert, wo keiner ist. Seit dem 19.08.2026 ist enforce die Vorgabe, auch ohne Konfigurationsdatei (cloak/cloak/config.py). In enforce führt ein Knoten über 1 MiB zu LimitExceeded, und eine Ausnahme im Detektor wird durchgereicht statt geschluckt. Unbekannte Konfigurationsschlüssel sind Fehler; Dateirechte des Tresors werden geprüft, nicht stillschweigend repariert.

Auf allen Wegen — mit Datum

cloak sitzt nicht an einer Stelle, sondern vor jedem Anbieter, den UltraNOS kennt. Die Reihenfolge, in der die Wege abgedeckt wurden, steht im Changelog:

  • 19.08.2026: Anthropic- und OpenAI-Draht (backend/llm/anthropic_provider.py, openai_provider.py, inklusive Werkzeugrunden), Editor-Vervollständigung, Produktion scharf. Eigene Anbieter mit OpenAI- oder Anthropic-Protokoll erben den Schutz.
  • 24.08.2026: Ollama als letzter HTTP-Anbieter nachgezogen (Commit fbd6960).
  • 31.08.2026: die CLI-Wege claude-cli und claude-instanz, also Claude Code als Anbieter (Commit fbd3d70, 17 Tests).
  • 03.09.2026: Präfixregeln für nano-gpt und Requesty (Commit 31761b8).

Gescannt wird auf dem Anthropic-Draht der System-Prompt, jeder Inhaltsblock der Nachrichten — Text, tool_use.input, tool_result.content —, die Werkzeugbeschreibungen und die Metadaten. Auf dem OpenAI-Draht die Inhalte aller Rollen einschließlich role="tool", die Argumente der Werkzeugaufrufe als geparstes JSON und die Werkzeugbeschreibungen (wire_openai.py). Der System-Prompt ist also inklusive: Ein Schlüssel in einer Projektnotiz, die als Kontext vorne angehängt wird, wird genauso ersetzt wie einer im Chat.

Nachprüfen statt glauben

Der einzige belastbare Nachweis heute ist GET /api/health (Details zur API unter /#developer). Das Feld cloak kennt drei Zustände: {aktiv:false}, {aktiv:true, bereit:false, fehler} und den vollen Zustand. Gemessen am 07.09.2026 gegen die gehostete Instanz:

"cloak": {
  "aktiv": true,
  "bereit": true,
  "profil": "enforce",
  "kategorien": ["credential", "email", "person", "secret", "secret_block"],
  "ersetzt": true
}

bereit:false ist dabei auch der Zustand „noch nicht geöffnet" — die Laufzeit initialisiert sich beim ersten Zug, nicht beim Start. Wer den Endpunkt direkt nach einem Neustart abfragt, sieht das und sollte es nicht als Ausfall lesen.

Je Zug schreibt das Backend eine Protokollzeile cloak: {treffer, platzhalter, ungescannt, notizen} — Zählwerte, nie Klartext. Der erste Live-Nachweis aus der Produktion vom 19.08.2026 (NanoGPT/GLM über den OpenAI-Draht) sah so aus: Das Modell antwortete mit dem Platzhalter statt mit dem GitHub-Testschlüssel, Protokoll treffer {secret/key.github: 2}, platzhalter 1, ungescannt 0.

Ein Beispiel, wie ein Prompt beim Anbieter ankommt:

Du schreibst:
  Warum schlägt der Aufruf fehl? In der .env steht
  ANTHROPIC_API_KEY=sk-ant-… und der Server meldet 401.
  Schreib mir an max.mustermann@example.org, wenn du fertig bist.

Der Anbieter sieht:
  Warum schlägt der Aufruf fehl? In der .env steht
  ANTHROPIC_API_KEY=CLOAK_SECRET_… und der Server meldet 401.
  Schreib mir an CLOAK_EMAIL_…, wenn du fertig bist.

Eine offene Baustelle, ehrlich benannt: Es gibt eine Prüfkopie je Zug unter GET /api/privacy/transparenz/{sitzung} mit Ersetzungen, Regel-ID, Fundort und dem verhüllten Prompt (backend/api/transparency_routes.py, eingehängt seit 03.09.2026). Die Route ist gebaut und getestet — 32 plus 74 plus 11 Tests im Repo —, aber der Erzeuger, der die Daten je Zug hineinschreibt, wird außerhalb der Tests noch nirgends aufgerufen. Das steht so in docs/OFFENE_PUNKTE.md. Die Route liefert deshalb heute leere Listen. Wir nennen sie hier, damit du sie nicht als Nachweis missverstehst.

Was cloak nicht kann

Diese Liste ist der Teil, ohne den der Artikel Werbung wäre. Sie stammt aus den Grenzen im Faktenblatt und aus den Quelldateien selbst.

  • Musterbasiert. Ein Format ohne Präfix, ohne Zuweisungskontext und mit Entropie unter 4,0 geht durch. Ein Passwort wie Sommer2026! ohne password= davor wird nicht erkannt. Deshalb verspricht die Produktbeschreibung bewusst nicht, dass Schlüssel das Modell in jedem Fall verfehlen; ein Frontend-Test (test_werbeaussagen.mjs) blockt die absolute Formulierung.
  • Keine Bilder, PDFs, base64. Ein Screenshot einer .env wird nicht gescannt. Thinking-Blöcke sind signiert und werden nur gezählt, nicht verändert.
  • Klarnamen im Fließtext nur über den Tresor (cloak add person). Ein naives Namensmuster hatte im Test 594 Fehltreffer allein in der Doku. Telefonnummern, IBAN und Kartennummern liefern Befunde, aber keinen Ersatz. Hostnamen, IPs und Pfade hängen nicht in der Pipeline.
  • Der CLI-Weg ist nur halb abgedeckt — und das ist heute der Weg der gehosteten Instanz. Laut /api/health vom 07.09.2026 sind auf ultranos.de zwei claude-instanz-Zugänge und der Betreiber-Zugang aktiv; anthropic-api, openai und ollama sind es nicht. Auf dem CLI-Weg werden Prompt und System-Prompt verhüllt, auch bei --resume. Aber die CLI führt Werkzeuge selbst aus und hält ihre eigene Verbindung zu Anthropic, die UltraNOS nicht beobachtet. Werkzeugergebnisse der CLI — genau die „ergiebigste Quelle" von oben — laufen dort nicht durch cloak. Ein Platzhalter im Werkzeugaufruf bleibt unaufgelöst und scheitert folgenlos. Die volle Abdeckung mit verhüllten Werkzeugergebnissen in jeder Runde und Rück-Ersetzung gilt auf den API-Wegen, die du mit eigenem Schlüssel wählst.
  • E-Mail und Person stehen wegen reveal=true im Klartext in der Nachrichtenablage von UltraNOS; person wird im Folgezug nicht zuverlässig erneut erkannt.
  • Latenz ist nicht gemessen. Belegt sind nur das Regelbudget von 2000 ms je Scan und eine Sperrzeit-Optimierung von 31,5 ms auf 0,01 ms in der Prüfkopie. Ein Messprotokoll für den Verhüllungspfad selbst gibt es im Changelog nicht.
  • Kein Sync, kein Export, kein TTL. Geht die Schlüsseldatei des Tresors verloren, sind die Einträge unlesbar. Die Erinnerungs-Extraktion hat ein eigenes, grobes Netz und „ersetzt cloak nicht" (backend/memory/erinnerung.py).

Was wir belegen können: 128 Tests in der cloak-Suite (cloak/tests/), 17 Tests für den CLI-Weg, ein Messlauf gegen das eigene Repo ohne Fehlalarme, ein Live-Nachweis aus der Produktion. Was wir nicht behaupten: Vollständigkeit.

Für wen das nichts ist

Wenn du ausschließlich lokale Modelle ohne Netzverbindung fährst, verlässt der Prompt deinen Rechner nicht; dann löst cloak ein Problem, das du nicht hast. Wenn dein Unternehmen mit dem Modellanbieter einen Enterprise-Vertrag mit Zero-Retention und eigener Auftragsverarbeitung hat, ist der Vertrag dein Hebel, und die Schwärzung ist höchstens eine zweite Leine. Und wer Geheimnisse hauptsächlich als Screenshots oder in PDFs weitergibt, bekommt von cloak heute keinen Schutz. Für alle dazwischen — Entwickler:innen, die Coding-Agenten mit Dateizugriff nutzen und den Prompt nicht einzeln kontrollieren können — ist der Ersatz vor dem Senden der Punkt, an dem sich der Unterschied entscheidet. cloak ist auf der Startseite als fester Bestandteil beschrieben; weitere Beiträge zu den Innereien findest du im Blog.

FAQ

Sieht das Modell den Schlüssel je?

Auf den API-Wegen (Anthropic, OpenAI, Ollama und eigene Anbieter mit diesen Protokollen) nicht, sofern ein Muster greift: Der Platzhalter geht raus, das Werkzeug bekommt den echten Wert erst bei der Ausführung, und das Ergebnis wird vor der nächsten Runde erneut verhüllt. Auf dem CLI-Weg gilt das nur für Prompt und System-Prompt; was die CLI selbst liest, sieht das Modell. Ein Format ohne erkennbares Muster geht auf jedem Weg durch.

Was ist mit Passwörtern in Terminal-Ausgaben?

Auf den API-Wegen läuft jede Werkzeugausgabe durch dieselbe Pipeline wie der Prompt; erkannt werden Passwörter mit Kontext, etwa password=…, mysql -p…, redis-cli -a …, --password …, Connection-Strings und Authorization-Kopfzeilen. Ein Passwort, das ohne solchen Anker allein in einer Zeile steht, wird nicht erkannt. Auf dem CLI-Weg werden Terminal-Ausgaben der CLI gar nicht gescannt.

Funktioniert das mit meinem eigenen API-Schlüssel?

Ja, und das ist der empfehlenswerte Weg. Trägst du in UltraNOS einen eigenen Anthropic- oder OpenAI-kompatiblen Schlüssel ein, läuft dein Verkehr über den API-Draht mit voller Abdeckung: Werkzeugergebnisse in jeder Runde, Rück-Ersetzung beim Aufruf. Die Vorgaben der Anbieter zur Datenhaltung findest du unter docs.anthropic.com und platform.openai.com/docs.

Ist das „DSGVO-konform"?

Das bescheinigt man sich nicht selbst, und wir tun es nicht. Belegbar ist: Serverstandort Nürnberg, ein Auftragsverarbeitungsvertrag nach Art. 28 DSGVO ist verfügbar, und Schlüssel, Zugangsdaten und E-Mail-Adressen werden vor dem Senden durch Platzhalter ersetzt, sofern ein Muster greift. Was der Modellanbieter mit dem Rest des Prompts tut, regelt dessen Vertrag mit dir. Dass Prompts an den gewählten Anbieter gehen, ändert cloak nicht.

Kann ich das selbst hosten?

Self-Hosting ist optional und auf Anfrage möglich — das Repo ist nicht öffentlich. Auf eigener Hardware läuft UltraNOS mit einem Startskript, cloak ist als Python-Paket ohne Laufzeitabhängigkeiten enthalten und wird über das Flag ULTRANOS_FEATURE_CLOAK eingeschaltet. Die Vorgabe des Flags ist aus; einmal an, gilt Fail-closed: Fehlt das Paket, scheitert der Zug, statt Klartext zu senden. Der Tresor liegt dann auf deinem Rechner, mit den oben genannten Grenzen bei Sync und Export.

Schlüssel bleiben bei dir — der Rest im Browser.

UltraNOS ist die Agentic Web IDE: mehrere KI-Agenten parallel, Editor, Git und Terminal. cloak ersetzt API-Schlüssel und Passwörter, bevor ein Prompt den Anbieter erreicht. Gehostet in Nürnberg, 14 Tage kostenlos.

14 Tage testen →

Weiterlesen