473 lines
27 KiB
Markdown
473 lines
27 KiB
Markdown
# Security-Konzept: Einschließen, Regeln, Überwachen
|
|
|
|
> **Rolle:** Bauplan. Stand 2026-09-19, entstanden aus dem Orchestrator-Vergleich
|
|
> (OpenClaw, Grok Bot, Claude Code/Codex, OpenHands, LlamaFirewall, CaMeL) und der
|
|
> Abstimmung mit dem Nutzer. Reihenfolge und Status stehen im Deploymentcenter, nicht
|
|
> hier.
|
|
|
|
## 1 — Das Ziel in einem Satz
|
|
|
|
**Ein bösartiger Agent — auch einer, der durch eingeschleusten Text übernommen wurde —
|
|
darf seinen Workspace nicht verlassen und nichts Schädliches tun.** Das muss auch dann
|
|
gelten, wenn Agenten eigene Skripte schreiben und ausführen.
|
|
|
|
## 2 — Der Grundsatz: das Modell ist nie die Grenze
|
|
|
|
Ein Überwachungsagent kann einen Ausbruch nicht verhindern, nur hinterher bemerken. Und
|
|
er ist selbst angreifbar: Er liest genau die Verläufe, in denen der eingeschleuste Text
|
|
steht. Deshalb vier Schichten, nach Kosten sortiert — die unteren drei sind
|
|
deterministisch und kosten keine Tokens, erst die oberste fragt ein Modell:
|
|
|
|
| Schicht | Was | Kosten | Verhindert / erkennt |
|
|
|---|---|---|---|
|
|
| 0 Einschließen | Sandbox-Runner für jede Codeausführung | keine Tokens | verhindert |
|
|
| 1 Regeln vor dem Aufruf | PermissionGate, Staging, Herkunftsregel, Netzprofil | keine Tokens | verhindert |
|
|
| 2 Regelbasierte Überwachung | Detektoren über das Audit, Köderwerte | keine Tokens | erkennt sofort |
|
|
| 3 Security-Agent | Einschätzung auf Anlass, Skriptprüfung, Stichprobe | gedeckelt | erkennt, bewertet |
|
|
|
|
So machen es die Systeme, die tragen: Claude Code und Codex setzen die Grenze im
|
|
Betriebssystem (bubblewrap bzw. Landlock + seccomp) und prüfen mit einem Modell nur
|
|
zusätzlich. OpenClaw zeigt die Gegenseite — dort ist die Sandbox *optional*, und die
|
|
Vorfälle mit ClawHub kamen genau daher.
|
|
|
|
### Warum das jetzt dran ist
|
|
|
|
Heute laufen alle Agenten **in einem Prozess unter einem Linux-Benutzer**. Die
|
|
Workspace-Grenze ist C#-Code in den Werkzeugen (`PathBoundary`); #373 hat gezeigt, wie
|
|
leicht sie bricht. Das trägt, solange Agenten keinen eigenen Code ausführen. Ein
|
|
Python-Skript fragt `PathBoundary` nicht — `open('/home/...')` geht am Werkzeug vorbei.
|
|
Und der Hauptprozess hält den entsperrten Hauptschlüssel.
|
|
|
|
**Regel, nicht verhandelbar: Fremder Code läuft nie im Hauptprozess.** Kein Skript,
|
|
keine Shell, kein MCP-stdio-Server ohne den Runner aus Schicht 0.
|
|
|
|
### Das Bedrohungsmodell wird um eine Klasse erweitert
|
|
|
|
Bisher: rein interner Betrieb; Schutz gegen Ordner-Kopieren und gegen Schadsoftware, die
|
|
nach Zugangsdaten sucht. Das bleibt. Neu: **Der Angreifer ist Inhalt, keine Person** —
|
|
eine Webseite, eine Mail, eine Chatnachricht, ein Tool-Ergebnis, das einen Agenten
|
|
umsteuert. Interner Betrieb schützt davor nicht, weil die Agenten Fremdinhalt lesen,
|
|
sobald sie ihre Arbeit tun.
|
|
|
|
## 3 — Schicht 0: Sandbox-Runner
|
|
|
|
Ein gemeinsamer Runner für alles, was Code ausführt: Shell (#196), freigegebene Skripte
|
|
(Abschnitt 7), MCP-stdio-Server (#197) und die heutigen `yt-dlp`/`ffmpeg`-Aufrufe des
|
|
`SocialMediaManagerTool`.
|
|
|
|
Garantien, jede mit einem Test, der den Ausbruch versucht:
|
|
|
|
- **Dateisystem:** Nur der Workspace des Agenten ist beschreibbar und sichtbar; System
|
|
schreibgeschützt, `$HOME`, Instanzverzeichnis, Schlüsselbund und die Workspaces anderer
|
|
Agenten sind nicht vorhanden (eigener Mount-Namespace, nicht bloß gesperrt).
|
|
- **Erbe:** Keine Umgebungsvariablen, keine offenen Dateideskriptoren, keine Secrets des
|
|
Hauptprozesses. Was ein Skript braucht, bekommt es ausdrücklich übergeben.
|
|
- **Netz:** Standardmäßig kein Netz (eigener Netz-Namespace). Freigabe nur über den
|
|
Egress-Proxy, siehe Abschnitt 5.
|
|
- **Grenzen:** Laufzeit, Speicher, Prozesszahl, Ausgabegröße; seccomp-Filter; kein
|
|
Rechteerwerb (`no_new_privs`).
|
|
- **Umsetzung:** `bwrap` unter Linux. Ein eigener Linux-Benutzer je Agent ist die
|
|
spätere Stufe, falls die Namespace-Trennung einmal nicht reicht.
|
|
|
|
- **Nichts, was der Hauptprozess liest, ist in der Sandbox beschreibbar.** Task-Dateien,
|
|
Skills, Identity/Soul, Skript-Manifeste, Gedächtnis-Importe und jede Konfiguration
|
|
liegen außerhalb des beschreibbaren Bereichs oder sind dort schreibgeschützt
|
|
eingebunden. Das ist die Lehre aus CVE-2026-25725 bei Claude Code: Code in der Sandbox
|
|
legte Hooks an, die beim nächsten Start mit vollen Rechten liefen. Eine Sandbox, die
|
|
Dateien schreiben darf, die draußen ausgeführt oder als Anweisung gelesen werden, ist
|
|
keine Grenze, sondern ein verzögerter Ausbruch. Ein Test gehört dazu: Die Sandbox
|
|
versucht jede dieser Dateien zu schreiben.
|
|
|
|
Unter Windows gibt es keinen Runner und damit **keine Codeausführung** — die Funktion
|
|
fehlt dort, statt schwächer zu sein.
|
|
|
|
*Gebaut am 2026-09-25 (#654), geprüft unter Ubuntu 26.04 mit bwrap 0.11.1 auf der
|
|
Test-VM.* Was beim Bau dazukam und in keinem Handbuch steht:
|
|
|
|
- **Die Wurzel der Sandbox ist beschreibbar**, wenn man nichts tut: bwrap baut sie als
|
|
eigenes tmpfs. Erst `--remount-ro /` nach allen Einbindungen macht daraus „nur `/work`
|
|
und `/tmp`“. Der Ausbruchstest hatte unter `/etc` eine Datei angelegt.
|
|
- **Der seccomp-Deskriptor wird weitergereicht.** bwrap liest den Filter über fd 3 und
|
|
lässt ihn im Programm offen; die innere Shell schließt ihn vor dem Start.
|
|
- **Die Prozessgrenze** (RLIMIT_NPROC) wird *innerhalb* des eigenen User-Namensraums
|
|
gesetzt. Dort zählt sie nur die Prozesse der Sandbox, nicht die Threads der Instanz
|
|
(auf der VM über tausend unter demselben Benutzer). Das ist mit einem Gegenfall
|
|
belegt: 5 von 10 gehen, 12 nicht. dash kennt dafür `ulimit -p`, bash `ulimit -u`.
|
|
- **`--die-with-parent` hängt am Thread, nicht am Prozess.** Das Elternsterbe-Signal
|
|
des Kernels feuert, wenn der *Thread* endet, der den Prozess gestartet hat. .NET
|
|
startet aus einem Threadpool-Thread, und unter Last baut der Threadpool Threads ab.
|
|
Folge auf der VM: bwrap bekam mitten im Lauf SIGKILL, und starb er noch im Aufbau,
|
|
lebte das Innere als Waise weiter. Alle Sandboxen starten deshalb aus einem eigenen,
|
|
dauerhaften Thread. Zum Beenden wird der ganze Baum unter bwrap aus `/proc`
|
|
eingesammelt und getötet, samt PID 1 des Namensraums, dessen Tod den Rest mitnimmt.
|
|
- **Erster Nutzer ist ffmpeg** im SocialMediaManager, weil es heruntergeladene Medien
|
|
zerlegt. yt-dlp braucht Netz und kommt mit dem Egress-Proxy (#655) in die Sandbox; bis
|
|
dahin läuft es wie bisher und das Protokoll sagt es.
|
|
|
|
## 4 — Schicht 1: Regeln vor dem Aufruf
|
|
|
|
Vorhanden: PermissionGate (Werkzeug je Agent), Staging mit Plan-Freeze, `PathBoundary`,
|
|
`UrlGuard`, `SqlGuard`. Neu:
|
|
|
|
### 4.1 Herkunftsregel
|
|
|
|
Vorbild CaMeL, in der einfachsten Form. Sobald ein Lauf **Fremdinhalt** gelesen hat —
|
|
WebFetch, Mail, Chat, eine Datei, die nicht der Agent selbst geschrieben hat, das
|
|
Ergebnis eines MCP-Servers —, gilt der Lauf als *belastet*. Ab dann ist jede Aktion mit
|
|
**Außenwirkung** freigabepflichtig, egal was die Policy sonst sagt: Mail senden, posten,
|
|
FTP-Upload, Schreiben in die Datenbank, Nachricht an andere Agenten, Task an andere
|
|
Agenten.
|
|
|
|
Das kostet nichts und nimmt einer übernommenen Anweisung genau die Wirkung, um die es
|
|
geht: Sie kann lesen und vorschlagen, aber nicht handeln. Welche Werkzeuge Fremdinhalt
|
|
liefern und welche Außenwirkung haben, erklärt jedes Werkzeug selbst (wie
|
|
`StagingAktion` seit #609). Die Freigabe-Nachricht (#644) nennt die Quelle, die den Lauf
|
|
belastet hat.
|
|
|
|
**Freigabemüdigkeit ist der eigentliche Gegner dieser Regel.** Der Sicherheitsvergleich
|
|
vom 2026-09-19 hat sie als realistischstes Einfallstor ausgemacht: Fast jeder Agent liest
|
|
Fremdinhalt, fast jeder Lauf wird belastet, und wer 40 Freigaben am Tag abnickt, ist
|
|
selbst der Umgehungsweg. Deshalb gehört zur Regel:
|
|
|
|
- **Feste Ziele sind ausgenommen.** Außenwirkung an Ziele, die ein Mensch vorab
|
|
eingerichtet hat — die eigenen Kanäle, `@human`, fest hinterlegte Empfänger je Agent —
|
|
bleibt ohne Freigabe. Eine Injection kann dorthin schreiben, aber niemanden erreichen,
|
|
den der Betreiber nicht selbst ausgewählt hat.
|
|
- **Muster-Freigabe auf Zeit.** Ein wiederkehrender Aufruf (gleicher Agent, gleiches
|
|
Werkzeug, gleiches Ziel) kann für eine begrenzte Frist freigegeben werden, statt jeden
|
|
Einzelfall zu bestätigen. Nie für hohe Risiken, nie ohne Ablauf.
|
|
- **Die Freigabequote wird sichtbar** (Abschnitt 10). Wer 100 % durchwinkt, hat keine
|
|
Kontrolle mehr, sondern eine Verzögerung — das soll man sehen, nicht vermuten.
|
|
- **Die Vorschau zeigt Herkunft.** Argumente, die Daten aus sensiblen Quellen tragen
|
|
(Gedächtnis, Datenbank, Dateien außerhalb von `out/`), werden in der Freigabe
|
|
hervorgehoben — sonst geht ein Abfluss, versteckt in einem langen Argument, durch die
|
|
Freigabe hindurch.
|
|
|
|
### 4.2 Schutz vor Amoklauf
|
|
|
|
Rate-Limits je Werkzeug und Agent, Circuit-Breaker bei gehäuften Fehlern oder
|
|
Ablehnungen — der Übergang zur Quarantäne (Abschnitt 6). Aus dem
|
|
[Erweiterungskonzept](Orchestrator-Erweiterungskonzept-2026.md).
|
|
|
|
### 4.3 Belastung wandert mit
|
|
|
|
Die Lücke, die der Vergleich aufgedeckt hat und die kein anderes Konzept schließt: Ein
|
|
belasteter Lauf schreibt einen Gedächtniseintrag, einen Skill-Entwurf oder einen Task.
|
|
Ein *späterer, unbelasteter* Lauf lädt ihn — und die eingeschleuste Anweisung wirkt
|
|
jetzt ohne Herkunftsmarke. Die Herkunftsregel wäre über einen Umweg ausgehebelt.
|
|
|
|
Deshalb: **Was ein belasteter Lauf hinterlässt, trägt die Marke weiter** — Gedächtnis,
|
|
Skill-Entwürfe, Tasks und Task-Kommentare, Dateien im SharedWorkspace. Die Engine
|
|
stempelt sie, nicht der Agent. **Wer einen markierten Eintrag lädt, wird selbst
|
|
belastet.** Ein Mensch kann einen Eintrag nach Prüfung entlasten; das steht im Audit.
|
|
Skill-Entwürfe aus belasteten Läufen sind in der Freigabe (#198) ausdrücklich markiert.
|
|
|
|
## 5 — Netzprofil je Agent
|
|
|
|
Ein Agent hat **drei getrennte Wege ins Netz**. Sie werden oft in einen Topf geworfen,
|
|
sind aber verschieden gefährlich und brauchen eigene Schalter.
|
|
|
|
| Weg | Wer ruft auf | Wer wählt das Ziel | Heute | Einstellung |
|
|
|---|---|---|---|---|
|
|
| **Modell** | die Engine, zu OpenRouter | niemand — immer OpenRouter | fest | Modell-Netzprofil (5.1) |
|
|
| **Werkzeuge** | der Hauptprozess (WebFetch, Mail, MCP …) | der Agent, im Rahmen der Allowlist | WebFetch mit Domain-Allowlist, muss je Agent freigegeben werden | bleibt je Werkzeug |
|
|
| **Sandbox** | ein Skript oder Shell-Befehl | der Code | gibt es noch nicht | aus; Freigabe je Skript (5.2) |
|
|
|
|
Der Weg zu OpenRouter ist **kein** Internetzugang des Agenten: Die Engine spricht mit
|
|
genau einem Ziel, der Agent kann weder Adresse noch Inhalt der Verbindung bestimmen. Er
|
|
wird aber dann zum Internetzugang, wenn das *Modell selbst* suchen darf.
|
|
|
|
### 5.1 Modell-Netzprofil (je Agent, in den Modelleinstellungen)
|
|
|
|
- **Websuche des Modells:** OpenRouter kann dem Modell eine eigene Websuche geben (Plugin
|
|
`web` oder das Suffix `:online` am Modellnamen). Das ist ein Netzweg, an dem weder
|
|
Staging noch Audit vorbeikommen. **Voreinstellung aus**; ein Modellname mit `:online`
|
|
wird abgelehnt, solange der Schalter aus ist.
|
|
- **Datennutzung beim Anbieter:** `data_collection: deny` als Voreinstellung (das Feld
|
|
ist in `ProviderPreferences` schon vorhanden, wird aber noch nicht gesetzt); optional
|
|
nur Anbieter ohne Datenspeicherung.
|
|
- **Anbieter-Allowlist und Ausweichen:** Welche Anbieter ein Agent nutzen darf, ob auf
|
|
andere ausgewichen werden darf (`allow_fallbacks`).
|
|
- `require_parameters` bleibt an (#550).
|
|
|
|
### 5.2 Sandbox-Netz
|
|
|
|
Standard: kein Netz. Braucht ein Skript Netz, **deklariert es die Ziele in seinem
|
|
Manifest** (Abschnitt 7), und die Freigabe des Skripts umfasst diese Ziele. Durchgesetzt
|
|
wird es vom Egress-Proxy: nur die deklarierten Domains, nur HTTPS, jeder Abruf im Audit.
|
|
Ein Skript, das etwas anderes erreichen will, scheitert und erzeugt einen Befund.
|
|
|
|
*Der Egress-Proxy ist gebaut (2026-09-25, mit #712), die Anbindung an Skript-Manifeste
|
|
noch nicht (#655, #695).* So umgesetzt:
|
|
|
|
- **Die Sandbox teilt nie das Netz des Hosts.** Mit geteiltem Netz sähe sie alles, was der
|
|
Host auf Loopback anbietet, darunter den Port der Weboberfläche und eine Datenbank. Sie
|
|
sähe außerdem abstrakte Unix-Sockets wie ein X11-Display, denn die gehören zum
|
|
Netz-Namensraum, nicht zum Dateisystem. Die Sandbox behält ihr Netz mit nur Loopback. Ein
|
|
Starter in der Sandbox (die Anwendung mit `--sandbox-launch`) lauscht dort auf
|
|
`127.0.0.1:3128` und reicht jede Verbindung an einen Unix-Socket weiter. Der Socket ist
|
|
eingebunden, dahinter steht der Proxy im Hauptprozess.
|
|
- **Nur `CONNECT host:port`**, also ein TLS-Tunnel. Klartext-HTTP wird abgewiesen. Der Host
|
|
muss auf der Liste stehen (`host`, `host:port`, `*.domain` nur für Subdomains).
|
|
Adressliterale stehen nie auf der Liste.
|
|
- **Namen löst der Proxy auf, nicht die Sandbox.** Löst ein gelisteter Name auch nur
|
|
teilweise auf eine nicht-öffentliche Adresse auf, wird er ganz abgewiesen. Das betrifft
|
|
Loopback, private Netze, CGNAT, Link-Local (Metadaten-Dienste), Multicast und
|
|
IPv6-Hüllen wie 6to4 und NAT64. So kommt kein DNS-Rebinding auf die Instanz.
|
|
- Jede Verbindung und jede Abweisung steht im Log. Im Audit steht sie noch nicht, das kommt
|
|
mit den Manifesten.
|
|
|
|
Geprüft unter Linux in der echten Sandbox (`SandboxLaunchTests`): Ein gelisteter Host
|
|
antwortet, ein anderer bekommt 403, `--noproxy` findet kein Netz, und ein Dienst auf dem
|
|
Loopback des Hosts ist nicht erreichbar.
|
|
|
|
## 6 — Befunde und Quarantäne
|
|
|
|
### 6.1 Zwei Stufen
|
|
|
|
| Stufe | Beispiele | Reaktion | Aufheben |
|
|
|---|---|---|---|
|
|
| **Niedrig** | gehäufte Ablehnungen, Kostensprung, neue Zieldomain, Änderung an Identity/Soul | Nachricht über den Kanal (#644) mit Quarantäne-**Vorschlag** — per Button bestätigen oder verwerfen | im Messenger oder in der Oberfläche |
|
|
| **Hoch** | Köderwert in einem ausgehenden Aufruf, Ausbruchsversuch aus der Sandbox, Zugriff auf fremde Workspaces, Skript weicht vom freigegebenen Hash ab | **sofortige Quarantäne**, Nachricht über den Kanal **ohne** Schaltfläche | **nur in der Oberfläche**, nach Prüfung |
|
|
|
|
Warum hoch nicht per Messenger: Der Messenger ist ein Kanal, über den auch Fremdinhalt
|
|
kommt, und ein Telefon geht leichter verloren als der Zugang zur Oberfläche. Wer einen
|
|
Agenten nach einem Köder-Treffer wieder freilässt, soll die Belege vor sich haben.
|
|
|
|
### 6.2 Was Quarantäne heißt
|
|
|
|
- Der Agent nimmt keine neuen Läufe an (Scanner überspringt ihn, Chat wird abgewiesen).
|
|
- Ein laufender Lauf endet beim nächsten Schritt; Sandbox-Prozesse des Agenten werden
|
|
beendet.
|
|
- Alle ausgehenden Nachrichten des Agenten werden zurückgehalten.
|
|
- Seine offenen Freigabe-Vorschläge werden gesperrt, nicht gelöscht.
|
|
- Alles ist umkehrbar und steht im Audit. Nichts wird gelöscht.
|
|
|
|
### 6.3 Ausnahmen für bekannte Befunde
|
|
|
|
Damit dieselbe Meldung nicht jeden Tag wiederkommt:
|
|
|
|
- Jeder Befund hat einen **Fingerabdruck** aus Detektor, Agent, Werkzeug und normalisiertem
|
|
Ziel. Gleiche Fingerabdrücke innerhalb eines Zeitfensters werden zu einem Befund mit
|
|
Zähler zusammengefasst, statt neu gemeldet zu werden.
|
|
- Eine **Ausnahme** gilt für einen Fingerabdruck, trägt Begründung, Urheber und ein
|
|
**Ablaufdatum** und steht im Audit. Ausnahmen ohne Ablauf gibt es nicht.
|
|
- Ausnahmen für niedrige Befunde gehen auch im Messenger („nicht mehr melden, 30 Tage"),
|
|
für hohe nur in der Oberfläche.
|
|
- **Nie ausnehmbar:** Köder-Treffer und Sandbox-Ausbruchsversuche.
|
|
|
|
## 7 — Skripte, die Agenten selbst schreiben
|
|
|
|
Vorgabe: Freigegebene Agenten dürfen eigene Skripte schreiben. Ausgeführt wird nur, was
|
|
geprüft ist; **jede Änderung, auch ein Bugfix, braucht eine neue Prüfung.**
|
|
|
|
- **Wer darf:** nur Agenten mit der Fähigkeit „Skripte schreiben". Standard aus.
|
|
- **Form:** Das Skript liegt im Workspace unter `scripts/`, daneben ein Manifest: Zweck,
|
|
Interpreter, Eingaben, Ausgaben, Netzziele (5.2), Laufzeitgrenze.
|
|
- **Bindung:** Die Freigabe gilt für den SHA-256 von Skript **und** Manifest **und** die
|
|
Interpreter-Fassung — nach dem Vorbild von OpenClaws Exec-Freigaben, die an Befehl,
|
|
Arbeitsverzeichnis und Datei-Hashes gebunden sind. Ändert sich ein Byte, passt der Hash
|
|
nicht mehr: nicht ausführbar, neue Prüfung. Versucht ein Agent, ein verändertes Skript
|
|
auszuführen, ist das ein hoher Befund.
|
|
- **Saubere Umgebung.** Der Hash allein reicht nicht: Im August 2026 wurde Claude Codes
|
|
Auto-Modus umgangen, indem ein Archiv eine Datei `struct.py` in das Arbeitsverzeichnis
|
|
legte, die beim Import eines Standardmoduls mitgeladen wurde („confused environment").
|
|
Ein freigegebenes Skript läuft deshalb **aus einer schreibgeschützten, sauberen Kopie**,
|
|
mit `python -I` (kein Arbeitsverzeichnis, kein `PYTHONPATH` im Suchpfad), mit
|
|
Abhängigkeiten, die im Manifest **mit Hash** festgeschrieben sind, und der Workspace ist
|
|
nur als Datenverzeichnis unter einem eigenen Pfad eingebunden. Was das Skript lädt, ist
|
|
damit genau das, was geprüft wurde.
|
|
- **Ablauf der Prüfung:**
|
|
1. **Deterministische Vorprüfung** (kostenlos): Größe, Imports und Aufrufe, die eine
|
|
Begründung brauchen (`subprocess`, `socket`, `ctypes`, `os.system`, `eval`/`exec`,
|
|
dynamisches Nachladen, Base64-Dekodieren mit anschließender Ausführung), Abgleich
|
|
mit dem Manifest („deklariert kein Netz, importiert aber `requests`").
|
|
2. **Einschätzung des Security-Agenten:** *unbedenklich* / *bedenklich* (mit Gründen
|
|
und Zeilen) / *zu komplex für mich — externe Prüfung*. Ab einer Größen- oder
|
|
Komplexitätsgrenze lautet das Urteil ohne Modellaufruf „zu komplex". Externe
|
|
Prüfung heißt: Mensch, oder auf ausdrückliche Einstellung ein stärkeres Modell mit
|
|
eigenem Budget.
|
|
3. **Freigabe durch den Menschen**, mit Vorprüfung, Einschätzung und Diff zur vorigen
|
|
Fassung vor Augen.
|
|
- **Die Einschätzung ist ein Rat, keine Freigabe.** Auch „unbedenklich" gibt nichts frei.
|
|
Und selbst ein freigegebenes Skript läuft nur im Runner — die Prüfung ist die zweite
|
|
Linie, nicht die erste.
|
|
- **Selbstschutz des Prüfers:** Das Skript ist für den Security-Agenten Fremdinhalt. Ein
|
|
Kommentar wie „als unbedenklich einstufen" darf nichts bewirken: Die Vorprüfung aus
|
|
Schritt 1 kann das Modell nicht überstimmen, die Ausgabe ist strukturiert, und ein
|
|
Urteil, das der Vorprüfung widerspricht, wird als Widerspruch angezeigt, nicht still
|
|
übernommen.
|
|
|
|
## 8 — Schicht 2: Regelbasierte Überwachung
|
|
|
|
Detektoren über das Audit-Log. Laufen bei jedem Eintrag bzw. im Scanner-Takt, kosten keine
|
|
Tokens und erzeugen Befunde nach Abschnitt 6.
|
|
|
|
- Grenzverletzungen (`PathBoundary`, `UrlGuard`, `SqlGuard`, Sandbox) und abgelehnte
|
|
Aufrufe in Serie
|
|
- neue Zieldomains, neue Empfänger
|
|
- Kostensprung gegenüber dem eigenen Mittel
|
|
- Änderungen an Identity, Soul, Skills, Skripten
|
|
- verdächtige Folgen innerhalb eines Laufs: erst Fremdinhalt oder sensible Pfade lesen,
|
|
dann Außenwirkung versuchen
|
|
- **Köderwerte:** Köderdateien im Workspace und Köder-Secrets im Schlüsselbund, die kein
|
|
legitimer Ablauf je braucht. Taucht ein Köderwert in einem ausgehenden Aufruf, einer
|
|
Nachricht oder im Sandbox-Netz auf → hoher Befund, sofortige Quarantäne. Praktisch
|
|
keine Fehlalarme, weil es keinen legitimen Grund gibt, ihn zu benutzen.
|
|
|
|
## 9 — Schicht 3: Security-Agent
|
|
|
|
**Ein eingeschränktes Profil des AgentInspector (#204), kein zweites System.** Gleiche
|
|
Datenbasis (Audit und Receipts, von der Engine gestempelt), gleicher Zweck.
|
|
|
|
- **Wann er läuft:** auf Anlass aus Schicht 2 (Befund bewerten, Zusammenhänge suchen),
|
|
bei jeder Skriptprüfung (Abschnitt 7) und einmal täglich als Stichprobe mit kurzer
|
|
Lagezusammenfassung. Nie dauerhaft mitlaufend.
|
|
- **Was er sieht:** strukturierte Audit-Daten — Werkzeug, Ausgang, Pfade, Ziele, Kosten.
|
|
Rohe Fremdtexte nur gekürzt und als Daten gerahmt (#193).
|
|
- **Was er darf — hart im Code, nicht im Prompt:** Befunde anlegen und einstufen, über
|
|
den Kanal melden, Quarantäne vorschlagen (niedrig) oder verhängen (hoch), Skripte
|
|
einschätzen. **Nie:** freigeben, Quarantäne aufheben, Ausnahmen anlegen, Regeln oder
|
|
Einstellungen ändern, Werkzeuge mit Außenwirkung benutzen.
|
|
- **Kosten:** günstiges Modell, eigenes Tagesbudget im Budget-Guard mit hartem Deckel.
|
|
Ist der Deckel erreicht, laufen Schicht 0 bis 2 unverändert weiter, nur Einschätzungen
|
|
bleiben aus — und das wird im Sicherheitsbereich angezeigt. Planbar wird es durch den
|
|
Deckel und dadurch, dass Schicht 2 vorfiltert.
|
|
|
|
## 10 — Sicherheitsbereich in Oberfläche und WebUI
|
|
|
|
Eigener Menüpunkt auf Instanzebene, in Avalonia und in der Weboberfläche gleich
|
|
aufgebaut:
|
|
|
|
- **Freigabequote** je Agent und Werkzeug: wie viel wird freigegeben, wie schnell, wie
|
|
oft abgelehnt. Eine Quote nahe 100 % bei hoher Zahl ist selbst ein Befund.
|
|
- **Lage:** Sandbox verfügbar, Schlüsselbund dicht, Agenten in Quarantäne, offene
|
|
Befunde, offene Skriptprüfungen, Kanalzustand (#645), Budget des Security-Agenten.
|
|
- **Befunde:** Liste mit Stufe, Fingerabdruck, Zähler, Beleg aus dem Audit; Quarantäne
|
|
aufheben (hoch: nur hier), Ausnahme anlegen.
|
|
- **Skripte:** ausstehende Prüfungen mit Vorprüfung, Einschätzung und Diff; freigegebene
|
|
Skripte mit Hash und Freigabe-Datum.
|
|
- **Richtlinien je Agent:** Netzprofil (Abschnitt 5), Herkunftsregel, Skript-Fähigkeit,
|
|
Sandbox-Grenzen.
|
|
- **Ausnahmen:** mit Ablauf, Urheber, Begründung.
|
|
- **Selbstprüfung auf Drift:** „Sandbox konfiguriert, aber nicht verfügbar", „Modell mit
|
|
`:online` bei ausgeschalteter Websuche", „Agent darf Skripte schreiben, Runner fehlt".
|
|
Vorbild `openclaw security audit`; die Prüfungen gehören zu den Bausteinen von #648.
|
|
|
|
Zwei Regeln:
|
|
|
|
- **Agenten kommen an diesen Bereich nicht heran** — auch AgentEditor und ein
|
|
Personalmanager-Agent nicht. Seine Einstellungen liegen außerhalb dessen, was ein
|
|
Werkzeug schreiben kann.
|
|
- **Wer eine Regel lockert, bestätigt ausdrücklich**, und die Änderung steht im Audit.
|
|
In der Weboberfläche verlangt das eine erneute Anmeldung.
|
|
|
|
## 11 — Zugangsdaten: der Agent sieht sie nie
|
|
|
|
*Ergänzt am 2026-09-22 nach einem Vorschlag des Nutzers.*
|
|
|
|
Heute bekommt ein Werkzeug seine Zugangsdaten aus der Konfiguration des Agenten. Wo ein
|
|
Agent eine fremde API bedienen soll, steht der Schlüssel in Reichweite dessen, was in
|
|
seinen Kontext geraten kann.
|
|
|
|
**Der Vorschlag: eine Zugangsdatenverwaltung in ClawdDotNet.** Der Agent bekennt nie
|
|
einen Wert zu sehen, sondern schreibt einen Platzhalter; ClawdDotNet setzt ihn im
|
|
Werkzeug ein, kurz bevor der Aufruf hinausgeht.
|
|
|
|
```
|
|
Agent: DirectAPI, Header: { "Authorization": "Bearer {{secret:amazon-sp-api}}" }
|
|
ClawdDotNet: ersetzt beim Aufruf, protokolliert die Benutzung, nie den Wert
|
|
Antwort: wird auf dem Rückweg maskiert, bevor sie in den Kontext geht
|
|
```
|
|
|
|
Das ist der richtige Ansatz, und er löst ein echtes Problem. Er löst aber nicht so viel,
|
|
wie er auf den ersten Blick verspricht — und die Lücken dazwischen sind die eigentliche
|
|
Arbeit.
|
|
|
|
### Wo es klemmt
|
|
|
|
**1. Ein Platzhalter darf nicht überall ersetzt werden.**
|
|
Ein stumpfes Suchen-und-Ersetzen über alle Argumente wäre die größte Falle: Dann schreibt
|
|
ein Agent `{{secret:amazon-sp-api}}` in einen Dateiinhalt, in ein SQL-Statement, in eine
|
|
Nachricht oder in eine URL — und der Wert landet dort, wo ihn jeder lesen kann. Die
|
|
Ersetzung gehört an **benannte Stellen je Werkzeug** (etwa `DirectAPI.headers`,
|
|
`Database.connection`), nie an beliebige Zeichenketten.
|
|
|
|
**2. Der Platzhalter schützt den Schlüssel, nicht das Ziel.**
|
|
Auch mit (1): Ein Agent, der den Schlüssel im Kopf `Authorization` verwenden darf, kann
|
|
die Anfrage an eine beliebige Adresse richten — und schickt das Geheimnis dorthin. Die
|
|
Verwaltung muss deshalb **jedes Geheimnis an ein Ziel binden**: „dieser Schlüssel gilt nur
|
|
gegen `sellingpartnerapi.amazon.com`". Ohne diese Bindung gibt der Platzhalter ein
|
|
falsches Gefühl von Sicherheit. Das Netzprofil je Agent (Abschnitt 5) ist die Hälfte
|
|
davon; die andere gehört an den Eintrag selbst.
|
|
|
|
**3. Die Antwort kann das Geheimnis enthalten.**
|
|
Manche Dienste spiegeln Token in Fehlermeldungen zurück, manche Bibliotheken schreiben
|
|
die Verbindungszeichenfolge in die Ausnahme (`Password=…`). Dann steht der Wert im
|
|
Werkzeugergebnis, im Kontext und im Transkript. **Die Maskierung auf dem Rückweg ist
|
|
keine Zugabe, sondern die zweite Hälfte des Mechanismus** — der allgemeine Fall ist #192,
|
|
der Anfang steht in #660.
|
|
|
|
**4. Was gilt als Verwendung durch einen Fremden?**
|
|
Ein Agent, der nie den Wert sieht, kann ihn nicht verraten — aber er kann ihn *benutzen*,
|
|
und dazu lässt sich ein Agent überreden. Der Platzhalter schützt gegen **Diebstahl der
|
|
Zugangsdaten**, nicht gegen **Missbrauch der Befugnis**. Dagegen helfen die Zielbindung,
|
|
die Freigaberegeln und die Herkunftsregel (#651) — nicht der Platzhalter.
|
|
|
|
**5. Die Verwaltung erbt die Schwäche des Hauptschlüssels.**
|
|
Eine Zugangsdatenverwaltung ist ein Tresor. Solange **#190** offen ist — der Hauptschlüssel
|
|
liegt unter Linux im Klartext —, legt man damit mehr Eier in einen Korb, von dem man
|
|
weiß, dass er offen steht. Der Tresor macht den Betrieb ordentlicher, die Sicherheit
|
|
verbessert er erst nach #190. Reihenfolge entsprechend.
|
|
|
|
**6. Nicht verraten, was es gibt.**
|
|
Fragt ein Agent nach `{{secret:buchhaltung-bank}}`, das ihm nicht zugeteilt ist, muss das
|
|
Werkzeug abweisen — **ohne zu sagen, ob dieses Geheimnis existiert**. Dieselbe Überlegung
|
|
wie beim Hinweis auf den anderen Arbeitsbereich (#662).
|
|
|
|
**7. Zuteilung ist eine Rechteänderung.**
|
|
Wer welches Geheimnis benutzen darf, entscheidet ein Mensch, nie ein Agent — auch keine
|
|
Abteilungsleitung. Die Zuteilung gehört an die Einheit (siehe Organisation-Konzept,
|
|
#683/#692), damit sechs Einkäufer sie einmal bekommen und die Buchhaltung den
|
|
Marktplatzschlüssel nicht sieht.
|
|
|
|
### Was der Tresor dafür zurückgibt
|
|
|
|
- **Eine Stelle zum Wechseln.** Ein rotierter Schlüssel wird einmal geändert, alle Agenten
|
|
arbeiten weiter.
|
|
- **Benutzungsspuren.** Wer hat wann welches Geheimnis gegen welches Ziel verwendet —
|
|
im Audit, ohne den Wert. Ein nie benutzter Eintrag kann weg; ein plötzlich viel
|
|
benutzter ist ein Befund.
|
|
- **Ein sauberer Weg für Skripte.** Ein freigegebenes Skript (#657) bekommt sein
|
|
Geheimnis als Umgebungsvariable vom Läufer, deklariert im Manifest — nie im Quelltext,
|
|
nie im Aufrufprotokoll (#696).
|
|
|
|
### Urteil
|
|
|
|
Bauen, ja — aber mit Zielbindung und Rückweg-Maskierung von Anfang an, und nach #190.
|
|
Ein Platzhalter allein verschiebt das Geheimnis nur von der Agentenkonfiguration in die
|
|
Werkzeugkonfiguration.
|
|
|
|
## 12 — Reihenfolge und Items
|
|
|
|
| Item | Vorhaben | Abschnitt |
|
|
|---|---|---|
|
|
| #651 | Herkunftsregel, mit Ausnahmen für feste Ziele, Muster-Freigabe und Herkunft in der Vorschau | 4.1 |
|
|
| #659 | Belastung wandert mit (Gedächtnis-Vergiftung) | 4.3 |
|
|
| #652 | Befunde und Quarantäne mit zwei Stufen, Ausnahmen | 6 |
|
|
| #653 | Regelbasierte Detektoren und Köderwerte | 8 |
|
|
| #654 | Sandbox-Runner | 3 |
|
|
| #655 | Netzprofil je Agent | 5 |
|
|
| #656 | Sicherheitsbereich in Avalonia und Weboberfläche | 10 |
|
|
| #657 | Skriptprüfung | 7 |
|
|
| #658 | Security-Agent (Profil von #204) | 9 |
|
|
|
|
1. **#651 mit #659, #652, #653** — größter Nutzen je Aufwand, braucht nur das Vorhandene und die
|
|
Messenger-Freigabe (#644).
|
|
2. **#654** — harte Vorbedingung für Skripte, Shell (#196) und MCP-stdio-Server (#197);
|
|
steht deshalb vor beiden.
|
|
3. **#655** und **#656**.
|
|
4. **#657** — mit #198, weil Skripte mit den Skills kommen.
|
|
5. **#658** — zuletzt, auf #204 aufbauend. Bis dahin prüft bei Skripten der Mensch
|
|
allein, mit der deterministischen Vorprüfung.
|