Default-Deny-Grenze
Wenn Code entscheidet, ob etwas eine Vertrauensgrenze passieren darf: liefert der unbekannte Fall den sicheren oder den bequemen Wert? Ziel: die Entscheidung so bauen, dass Vergesslichkeit in Richtung „verweigern” fehlschlägt, nicht in Richtung „durchlassen”.
Merkmal
Eine Aufzählung des nachweislich Erlaubten, und ein Rückfall, der verweigert:
- Der
switch/die Schleife listet nur, was geprüft wurde. - Der Default-Zweig gibt
false/errzurück — nie „ist schon okay”. - Keine Normalisierung vor dem Vergleich. Kein Trimmen, kein Kleinschreiben. Wer etwas anderes schickt als die Liste, meint etwas anderes.
- Neue Fälle müssen aktiv eingetragen werden. Wer einen vergisst, bekommt Verweigerung — nicht ein Leck.
Das Gegenteil (Deny-List / Default-Allow) verlagert die Beweislast: man müsste alles Gefährliche kennen. Man kennt es nie vollständig.
Belegte Fälle (bridgekit)
| Grenze | Richtung | Stelle | Rückfall bei Unbekanntem |
|---|---|---|---|
| Scrubber: darf ein OBX-Wert ungemaskiert raus? | ausgehend (→ Claude-API) | internal/scrubber/scrubber.go · obxWertUnbedenklich | return false → wird maskiert |
| Aktionen: darf ein Kommando laufen? | eingehend (Request → Shell) | internal/dienste/aktionen.go · findeAktion | return aktion{}, false → ErrUnbekannteAktion, 400, nichts läuft |
Beide sichern entgegengesetzte Richtungen derselben Prozessgrenze. bridgekit spricht mit genau zwei Außenwelten (Claude-API, docker) — jede hat ein Default-Deny-Tor. Zusammen ergeben sie eine geschlossene Hülle.
Das eigentliche Bedrohungsmodell
Beide Stellen benennen im Kommentar nicht den Angreifer, sondern die eigene Bequemlichkeit von morgen (ADR-008). Der Header von aktionen.go sagt es wörtlich: wer „nur schnell den Namen durchreichen” will, baut eine Remote-Shell auf 127.0.0.1. Die Struktur ist genau das, was diese Abkürzung verhindert.
Diagnosefrage für fremden Code: Was passiert, wenn ich einen Fall vergesse — kommt eine Verweigerung oder ein Leck?
Wachpunkt: eine Default-Deny-Grenze kann von woanders aufgeweicht werden
Scrubber.Maskiere verlangt zuerst, dass hl7.Parse die Nachricht akzeptiert. Die Fail-closed-Eigenschaft des Scrubbers hängt damit an der Strenge eines fremden Pakets, das davon nichts weiß.
Wie eng die Abhängigkeit wirklich ist, war nur per Mutation zu klären, nicht per Lesen. Der Scrubber hat ein zweites Tor (trennerAus), das drei der vier Parse-Ablehnungen unabhängig wiederholt. Tragend bleibt eine einzige Prüfung: fehlt MSH-2 (Kodierzeichen), lehnt nur Parse ab. Details und Belege: ADR-009.
Verallgemeinert, und das ist die eigentliche Lehre — sie hat zwei Stufen, weil ich in beide Fallen getappt bin:
- Eine Default-Deny-Grenze, die ihre Ablehnung von einem anderen Modul bezieht, ist nur so streng wie dieses Modul. Diese Abhängigkeit gehört benannt — sonst weicht sie jemand auf, der von der Grenze nichts weiß.
- Welche Prüfung tragend ist, lässt sich nicht durch Hinschauen entscheiden. Redundante Tore verschleiern es. Die plausible Erzählung („der Scrubber hängt am Parser”) war zu grob; erst die Mutation — jede Prüfung einzeln deaktivieren, schauen welcher Test rot wird — trennt tragend von doppelt gesichert.
- Und dann noch einmal: Auch die Begründung warum eine Prüfung trägt, ist eine Behauptung und braucht ihre eigene Gegenprobe. Zweite ADR-Fassung: „ohne MSH-2 zerlegt der Scrubber mit falschen Feldgrenzen und ein Name leckt.” Klang zwingend. War falsch —
trennerAusfällt auf^/~zurück, und das sind die Standardtrenner, der Name wird maskiert. Der echte Wert der Prüfung ist bescheidener: fail-closed gegenüber kaputter Eingabe, der Scrubber muss nie raten.
Ein Mechanismus, den man sich überzeugend erzählen kann, ist damit noch nicht ausgeführt worden. Die Gegenprobe gehört an die Behauptung, nicht nur an den Test.
Diagnose-Rezept
- Grenze finden: Wo entscheidet Code über Passieren/Verweigern? Liefert der unbekannte Fall
false? - Fremde Abhängigkeiten der Verweigerung auflisten (fremder Parser, fremde Validierung, fremde Liste).
- Jede einzeln kaputtmachen und den Testlauf beobachten. Was rot wird, ist tragend und gehört in ein ADR. Was grün bleibt, ist Verteidigung in der Tiefe — und darf nicht als Schutz verbucht werden.
- Sicherheit der Aussage: [x] Fakt [ ] Hypothese [ ] Spekulation (Beide Default-Deny-Fälle am Quelltext verifiziert; die tragende Prüfung der Parser→Scrubber-Kopplung per Mutations-Gegenprobe isoliert, siehe ADR-009.)