# Die Bindung von Genehmigungsanfragen verhindert Änderungen nach dem Klick

Eine menschliche Genehmigung ist nur dann sinnvoll, wenn sie eine Aktion freigibt, die sich danach nicht mehr ändern kann. Wenn zunächst die von einem Agenten vorgeschlagene HTTP-Anfrage oder der SSH-Befehl angezeigt wird, derselbe Agent die endgültigen Details aber später übermitteln darf, bleibt eine Lücke zwischen Prüfung und Nutzung, durch die sich eine zerstörerische Anfrage bequem einschleusen lässt.

Ich habe diesen Fehler schon in beruhigender Verkleidung gesehen: ein ansprechend gestaltetes Genehmigungsblatt, ein Ausweis für die Prozessidentität, ein grüner Bestätigungsstatus. Danach schickt die Implementierung ein beim Client liegendes Genehmigungstoken über das Protokoll zurück und akzeptiert daneben eine neue URL oder einen neuen Befehl. Die Person hat eine Sache genehmigt. Der Executor hat eine andere ausgeführt. Das ist ein fehlerhaftes Autorisierungsdesign, auch wenn niemand es absichtlich umgehen wollte.

## Eine Vorschau muss ein unveränderliches Autorisierungsobjekt beschreiben

Der Genehmigungsbildschirm muss ein gespeichertes Aktionsobjekt darstellen, und der Executor muss nach der Genehmigung genau dieses Objekt ausführen. Der Bildschirm zeigt den maßgeblichen Zustand an. Er fordert den Client nicht dazu auf, seinen Vorschlag später noch einmal sorgfältiger zu wiederholen.

Erstelle das Objekt, bevor eine Genehmigung erscheint. Vergib eine zufällige Aktions-ID, verknüpfe sie mit dem anfordernden Prozess und der Sitzung, löse die kanalspezifischen Eingaben auf und bewahre sie unter der Kontrolle des Executors im Speicher oder in einem verschlüsselten lokalen Speicher auf. Der Client kann einen Status wie „ausstehend“ und eine ID erhalten. Er sollte keine Berechtigung erhalten, mit der er später neue Ausführungsparameter anhängen kann.

Ein hilfreiches Denkmodell ist ein Restaurantbon, keine Einverständniserklärung. Nachdem der Kellner den Bon als genehmigt markiert hat, bittet die Küche den Gast nicht, das Essen noch einmal zu beschreiben. Sie bereitet den Bon zu, den sie vorliegen hat. Ändert sich die Bestellung, erhält die Küche einen neuen Bon und der Gast sieht die Änderung.

Das unveränderliche Objekt sollte mehr enthalten als die Felder, die in einer kompakten Vorschau sichtbar sind. Es braucht auch die Prozessidentität, die Sitzungs-ID, Erstellungszeit, Ablaufzeit, den Kanal, die Referenz auf die Zugangsdaten, normalisierte Aktionsdetails und einen Bindungs-Digest. Der Digest macht versehentliche Veränderungen und absichtliche Ersetzungen erkennbar. Die eigentliche Sicherheitsarbeit leistet jedoch, dass der Executor das Objekt besitzt.

Eine Genehmigungsantwort sollte daher nur Folgendes bedeuten: „Aktion 7c91... mit Bindungs-Digest sha256:... genehmigen.“ Sie darf nicht bedeuten: „Dieser Prozess darf einen HTTP-Aufruf ausführen“ oder „Der Aufrufer darf später den Befehl X ausführen.“ Solche umfassenderen Aussagen sind bei der Sitzungsautorisierung sinnvoll, können aber die Zustimmung zu einer bestimmten Nebenwirkung nicht ersetzen.

Dieser Unterschied ist bei autonomen Agenten besonders wichtig, weil der Client kein vertrauenswürdiger Sachbearbeiter ist, der eine menschliche Entscheidung zuverlässig abschreibt. Er ist ein aktives Programm, das Wiederholungen ausführen, Folgeaufrufe zusammenstellen, seinen Plan nach einem Tool-Ergebnis ändern oder schlicht einen Fehler enthalten kann. Behandle jedes Feld, das er nach der Vorschau noch beeinflussen kann, als verdächtig.

## Eine HTTP-Vorschau muss jede wirksame Eingabe nennen

Eine HTTP-Genehmigung bindet Methode, vollständiges Ziel, wirksame Header, Body und das Versandverhalten, die die Bedeutung der Anfrage verändern können. Eine Vorschau mit dem Text „POST an den Abrechnungsdienst“ liefert viel zu wenig Informationen, um einen zustandsverändernden Aufruf zu genehmigen.

RFC 9110 trennt Anfragemethode, Ziel, Felder und Inhalt, weil Server jedem davon eine andere Bedeutung geben können. Autorisierungscode sollte diese Trennung beibehalten. Formuliere nicht einen angenehmen Satz für das Genehmigungsblatt und überlasse die unangenehmen Details anschließend einer tieferen Schicht.

Erfasse bei einer ausgehenden Anfrage nach der Auflösung von Vorlagen und Standardwerten diese Werte:

- Schema, Hostname, Port, Pfad und Query-String.
- HTTP-Methode und die Information, ob die Anfrage Weiterleitungen folgen darf.
- Jeden wirksamen Headernamen und -wert, ausgenommen geheime Werte, die das Tresor injiziert.
- Die finalen Body-Bytes, ihren Inhaltstyp, ihre Länge und ihren Digest.
- Die Referenz auf die Zugangsdaten und den Injektionsmodus, etwa Bearer oder benutzerdefinierter Header.

Der Zugangswert gehört weder in die Vorschau noch in den Kontext des Agenten. Die Person braucht trotzdem genügend Informationen, um seine Verwendung zu beurteilen. „Produktionszugangsdaten für Zahlungen werden nur als Bearer-Token an api.example.test gesendet“ vermittelt etwas anderes als „Zugangsdaten angehängt“. Der Executor kann diese Beschreibung aus einem Tresordatensatz erzeugen, ohne das Token offenzulegen.

Genehmige keine nicht aufgelöste Vorlage. Angenommen, der Agent schlägt `POST /users/{id}/role` mit einem Body vor, der `{role}` enthält. Wenn eine andere Komponente diese Variablen erst nach der Genehmigung ersetzt, kann sie das tatsächliche Ziel oder die Berechtigung verändern. Löse die Variablen zuerst auf und erstelle dann das Aktionsobjekt. Wenn eine Eingabe absichtlich erst später verfügbar ist, fordere die Genehmigung an, sobald sie konkret wird.

Header verdienen mehr Aufmerksamkeit, als die meisten Teams ihnen schenken. Ein doppelter Header, ein geänderter `Content-Type`, ein zusätzlich eingefügter Override-Header oder eine andere Query-Kodierung können das Serververhalten verändern, während die freundliche Zusammenfassung gleich aussieht. Bewahre wiederholte Felder in ihrer Reihenfolge auf, wenn das Protokoll oder der Zielserver der Reihenfolge eine Bedeutung gibt. Leite mehrdeutige Eingaben nicht stillschweigend durch Zusammenfügen mit Kommas zusammen, sondern lehne sie ab.

Eine sinnvolle interne Datenstruktur könnte so aussehen. Das ist ein Implementierungsmuster, kein Vertrag für einen Endpunkt:

```
{
  "action_id": "7c91e2d4",
  "binding": "sha256:4f06...",
  "caller": {"session": "p-481", "process_start": "..."},
  "http": {
    "method": "PATCH",
    "url": "https://api.example.test/v1/users/42",
    "headers": [["content-type", "application/json"]],
    "body_sha256": "a4d8...",
    "body_length": 31,
    "redirects": "deny"
  },
  "credential_ref": "vault:payments-prod"
}
```

Nachdem die Person dieses Objekt genehmigt hat, liest die Transportschicht `http` und `credential_ref` aus ihrem eigenen gespeicherten Datensatz. Sie akzeptiert vom Client keine zweite URL, keine neue Header-Map und keinen anderen Body. Diese letzte Regel verhindert den Fehler. Der Digest beweist nur, auf welchen Datensatz sich die Genehmigung bezog.

## SSH-Befehle brauchen ihren aufgelösten Ausführungskontext

Eine SSH-Befehlsvorschau muss den Kontext der Remote-Ausführung binden, denn ein Befehlsstring allein sagt der Person nicht, was tatsächlich ausgeführt wird. Derselbe Text kann unter einem anderen Benutzer, auf einem anderen Host, in einer anderen Shell, in einem anderen Arbeitsverzeichnis, mit einer anderen Umgebung oder einem anderen stdin-Stream eine andere Wirkung haben.

RFC 4254 definiert eine Anfrage im SSH-Verbindungsprotokoll zur Befehlsausführung, schafft aber keine verständliche Genehmigungsgrenze für Menschen. SSH überträgt einen Befehlsstring an den Server. Die Interpretation durch eine Remote-Shell, die Kontokonfiguration, erzwungene Befehle und Befehls-Wrapper können eine Bedeutung hinzufügen, die eine einfache Vorschau nicht zeigt.

Speichere und zeige Hostname, Port, Remote-Benutzer, Erwartung an die Host-Authentifizierung, Identitätsreferenz, exakte Befehls-Bytes, Ausführungsmodus, Terminalzuweisung, Arbeitsverzeichnis, bereitgestellte Umgebung sowie Digest und Länge von stdin an. Bei einer Dateioperation über einen SSH-Helfer müssen außerdem der Remote-Pfad, der Operationstyp, der Datei-Digest und das Verhalten bei Überschreiben gebunden werden.

Der Ausführungsmodus ist keine Nebensache. Diese beiden Varianten sind nicht austauschbar:

```
/usr/local/bin/deploy --environment=staging
sh -lc '/usr/local/bin/deploy --environment=staging'
```

Die erste Variante fordert eine direkte Ausführung an, sofern der Helfer das unterstützt. Die zweite bittet ausdrücklich eine Shell, den String zu interpretieren. Shell-Parsing führt zu Expansionen, Befehlsersetzung, Verhalten beim Shell-Start und eigenen Regeln für Anführungszeichen. Wenn dein Produkt beide Modi akzeptiert, kennzeichne sie klar und übersetze den einen nach der Genehmigung niemals in den anderen.

Erstelle die Vorschau nicht aus einem für die Anzeige formatierten Befehl und führe anschließend ein separat zusammengestelltes Argument-Array aus. Ein Anzeigestring kann ein leeres Argument, einen Zeilenumbruch, ein nicht druckbares Zeichen oder den Unterschied zwischen `--file=/tmp/a b` und zwei Argumenten verbergen. Erzeuge aus genau der Bytefolge oder dem Argumentvektor, den der Helfer verwenden wird, eine sichere Darstellung mit Escaping. Enthält der Befehl Bytes, die sich nicht sicher anzeigen lassen, lehne ihn ab oder zeige eine ausdrücklich kodierte Darstellung, die der Benutzer prüfen kann.

Änderungen am Remote-Ziel erfordern dieselbe Sorgfalt wie HTTP-Weiterleitungen. Ein lokaler SSH-Konfigurationsalias, ein Proxy-Jump oder eine Suche in der Hostkonfiguration kann einen kurzen Hostnamen in ein anderes Ziel auflösen. Löse den endgültigen Verbindungsplan vor der Genehmigung auf und binde ihn. Wenn DNS-Rebinding zu deinem Bedrohungsmodell gehört, ergänze vorsichtig eine eigene Richtlinie für Hostadressen. Das Festlegen einer Adresse kann normale Ausfallsicherung verhindern, während das Ignorieren eines manipulierten Resolvers einen gültigen Hostnamen an die falsche Maschine schicken kann. Tu nicht so, als würde eine Befehlsvorschau dieses separate Problem lösen.

## Die gefährliche Zeitspanne beginnt nach dem Klick

Der Fehler entsteht, wenn die Benutzeroberfläche die vorgeschlagenen Details prüft, ein Ja aufzeichnet und ein späterer Ausführungspfad veränderliche Details vom Client liest. Oft versteckt er sich hinter ganz gewöhnlichem asynchronem Code.

Betrachte einen Agentenprozess, der ein Gateway um eine Geldüberweisung bittet:

1. Der Client sendet `POST https://api.example.test/transfers` mit einem Body für 50 Einheiten.
2. Das Gateway erstellt eine Vorschau und wartet auf die Genehmigung durch eine Person.
3. Die Person genehmigt die Anfrage, nachdem sie Ziel und Betrag gelesen hat.
4. Der Client sendet `execute` mit der Genehmigungs-ID und einem neuen Body für 5.000 Einheiten.
5. Das Gateway prüft nur, ob die Genehmigungs-ID gültig ist, injiziert die Zugangsdaten und sendet den neuen Body.

Für diese Abfolge braucht es weder eine manipulierte Benutzeroberfläche noch gestohlene Zugangsdaten. Der Client hat eine vom Gateway angebotene API verwendet. Ein Test, der die ursprüngliche Anfrage genehmigt und nur prüft, ob die ID funktioniert, wird bestehen. Ein Test, der die finalen ausgehenden Bytes mit den durch das genehmigte Objekt dargestellten Bytes vergleicht, wird fehlschlagen. Genau das soll passieren.

Derselbe Fehler tritt auf, wenn ein Gateway ein veränderbares Anfrageobjekt per Referenz speichert. Ein Vorschau-Worker liest es, ein Retry-Handler auf Agentenseite verändert es und der Versand-Worker sendet später die bearbeitete Version. Eine Kopie reicht nicht aus, wenn verschachtelte Maps, Body-Puffer oder Callbacks weiterhin gemeinsam verwendet werden. Erzeuge einen unveränderlichen Wert ohne Referenzen im Besitz des Clients oder serialisiere ihn in einer maßgeblichen Darstellung und rekonstruiere ihn nur aus dieser Darstellung.

Eine Ablaufzeit repariert keinen veränderlichen Zustand. Eine Genehmigung, die dreißig Sekunden gültig ist, kann auch eine geänderte Anfrage freigeben, die in der ersten Sekunde eingereicht wird. Ratenbegrenzungen reparieren den Fehler ebenfalls nicht. Sie verringern nur, wie oft ein Client einen Fehler ausnutzen kann, nicht ob die Person der Wirkung zugestimmt hat.

Halte die Zustandsmaschine klein genug, um sie zu prüfen:

```
proposed -\u003e frozen -\u003e awaiting_human -\u003e approved -\u003e dispatched
                         |                 |
                         v                 v
                      rejected           expired
```

Nur der Übergang von `approved` zu `dispatched` darf die externe Aktion ausführen. Dieser Übergang muss den eingefrorenen Datensatz über die Aktions-ID laden, Aufrufer und Ablaufzeit erneut prüfen und den Datensatz direkt an den HTTP- oder SSH-Executor übergeben. Jede Anfrage, Details zu ändern, geht zurück nach `proposed` und erzeugt eine neue ID.

## Binde die Bytes, wenn die Semantik Spielraum für Abweichungen lässt

Eine Bindung braucht eine stabile Darstellung. Sonst können zwei Komponenten glauben, dieselbe Anfrage genehmigt zu haben, und dennoch unterschiedliche Wire-Daten erzeugen. Die schwierige Frage ist, welche Darstellung maßgeblich sein soll.

Bei einfachen JSON-Anfragen normalisierst du eine strukturierte Form nach dokumentierten Regeln, serialisierst sie einmal und bewahrst die finalen Body-Bytes samt Digest auf. Die Vorschau kann lesbares JSON anzeigen, während der Versandpfad die aufbewahrten Bytes sendet. Parse, formatiere und serialisiere nach der Genehmigung niemals erneut, außer die umgewandelte Ausgabe war genau das genehmigte Objekt.

Bei Formularen, Multipart-Uploads, wiederholten Headern oder beliebigen Binäreingaben sind Bytes meist die sicherere Grenze. Binde die exakten Bytes, den Inhaltstyp und die Entscheidungen zur Übertragungsrahmung, die die Interpretation durch den Server beeinflussen. Ein Digest erlaubt es der Benutzeroberfläche, einen großen Body zu identifizieren, ohne ein privates Dokument in ein Genehmigungsblatt zu kopieren. Der Executor muss diese exakten Bytes aber aus einem vertrauenswürdigen Zustand aufbewahren oder erneut erzeugen.

Die Kanonisierung von URLs ist eine weitere Falle. Die Groß- und Kleinschreibung eines Hostnamens zu normalisieren, ist im Allgemeinen harmlos. Einen Pfad zu dekodieren und neu zu kodieren, Query-Paare zu sortieren, einen leeren Wert zu entfernen oder `+` und `%20` als identisch zu behandeln, kann jedoch verändern, wie eine Anwendung eine Anfrage weiterleitet oder prüft. Lege eine eng definierte kanonische Form fest, dokumentiere sie und lehne Eingaben ab, die mehr als eine plausible Interpretation haben. „Wir normalisieren URLs“ ist keine Sicherheitseigenschaft.

Bei SSH ist ein Argumentvektor besser als ein Shell-String, wenn der Protokollhelfer ihn ohne Shell ausführen kann. Binde jedes Argument als eigene Bytefolge. Wenn die Remote-Seite notwendigerweise einen einzelnen Shell-Befehlsstring erhält, binde diesen String exakt und stelle sein Escaping wahrheitsgetreu dar. Behaupte nicht, dass ein Parser auf der lokalen Maschine beweisen kann, was eine beliebige Startdatei der Remote-Shell tun wird.

Der Genehmigungs-Digest sollte eine versionierte und domänentrennende Kodierung abdecken. Stelle den kodierten Daten ein festes Label wie `action-v1/http` oder `action-v1/ssh` voran, nimm alle unveränderlichen Felder mit eindeutigen Längen oder einem deterministischen Serializer auf und hashe anschließend alles. Die Versionierung verhindert, dass eine alte Interpretation nach einem Upgrade stillschweigend zu einer neuen wird. Die Domänentrennung verhindert, dass ein HTTP-Objekt jemals mit einem SSH-Objekt übereinstimmt, nur weil ihre serialisierten Bytes zufällig identisch sind.

## Aufruferidentität und Aktionsbindung lösen unterschiedliche Probleme

Die Autorisierung pro Sitzung teilt dir mit, welcher laufende Prozess Aktionen anfordern darf. Die Aktionsbindung teilt dir mit, welche exakte Aktion dieser Prozess ausführen darf. Du brauchst beides, und keines ersetzt das andere.

Binde die Aktion an die vom Gateway beobachtete Prozessidentität, nicht an einen Namen, den der Client übermittelt. Unter macOS können dazu die Codesignatur, die Prozess-ID und die Startinstanz des Prozesses gehören. Die Startinstanz ist wichtig, weil ein Betriebssystem eine Prozess-ID wiederverwenden kann. Wenn der Prozess endet, verwirf seine ausstehenden und genehmigten Aktionen zusammen mit der Sitzungsberechtigung.

Lass einen Kindprozess nicht allein deshalb eine umfassende Genehmigung übernehmen, weil er die ID seines Elternprozesses kennt. Beobachte jeden verbindenden Prozess an der Grenze des Gateways. Wenn eine Tool-Architektur das unmöglich macht, benenne die Einschränkung klar und verkürze lieber die Genehmigungsdauer, statt eine Sicherheit vorzutäuschen, die du nicht hast.

Auch die menschliche Genehmigung braucht eine kurze, begrenzte Gültigkeitsdauer. Die Ablaufzeit gehört in das eingefrorene Objekt und in die Genehmigungsentscheidung, nicht nur in einen Timer innerhalb der Benutzeroberfläche. Nach Ablauf muss der Executor den Versand ablehnen, selbst wenn der Client die alte Antwort die ganze Zeit aufbewahrt hat. Eine widerrufene Sitzung sollte ebenfalls verhindern, dass bereits genehmigte, aber noch nicht verwendete Aktionen ausgeführt werden.

Die sitzungsbezogene Autorisierung von Sallyport kann klären, ob ein neu verbundener Agentenprozess einen Lauf überhaupt verwenden darf. Ein Aktions-Gateway braucht für jede vorgeschlagene Nebenwirkung trotzdem diese separate unveränderliche Grenze. Eine Sitzungsentscheidung ist absichtlich umfassend. Die Genehmigung eines HTTP-Aufrufs oder SSH-Befehls muss exakt bleiben.

Lass die Prozessidentität kein Grund sein, weniger Details anzuzeigen. Auch signierter, vertrauenswürdiger Code kann Fehler enthalten, unerwartete Prompt-Injection-Eingaben verarbeiten oder ein Plugin ausführen, das einen anderen Pfad genommen hat als vom Autor erwartet. Der Wert eines menschlichen Prüfpunkts liegt darin, dass die konkrete Wirkung sichtbar wird, bevor sie die Maschine verlässt.

## Wiederholungen und Weiterleitungen müssen innerhalb der genehmigten Grenze bleiben

Eine Transportwiederholung darf eine Genehmigung nur dann wiederverwenden, wenn sie dieselbe eingefrorene Aktion unter den protokollierten Bedingungen erneut sendet. Viele Retry-Implementierungen verletzen diese Regel unbemerkt, indem sie Anfragen aus einem veränderlichen Client-Zustand neu aufbauen.

Lege das erlaubte Wiederholungsverhalten fest, wenn du die Aktion einfrierst. Erlaube zum Beispiel eine erneute Übertragung nach einem Verbindungsfehler, bevor eine HTTP-Antwort eingetroffen ist, mit derselben URL, denselben Headern, denselben Body-Bytes und derselben Referenz auf die Zugangsdaten. Zeichne jeden Versuch unter derselben Aktions-ID auf. Wiederhole eine nicht idempotente Anfrage nach einer unklaren Antwort nicht, außer Protokoll und Ziel-API bieten einen verlässlichen Idempotenzmechanismus.

Ein Idempotenz-Token ist nur dann nützlich, wenn es selbst gebunden ist. Erzeugt der Executor es, muss er es beim Einfrieren der Aktion erzeugen und in der genehmigten Header-Menge aufbewahren. Liefert der Client es, behandle es als Teil der genehmigten Anfrage. Wird es bei der Wiederholung ersetzt, kann eine Genehmigung eine zweite Nebenwirkung auslösen.

Auch die Wiederherstellung der Authentifizierung verdient Misstrauen. Wenn ein Tresor intern Zugangsdaten erneuert, aber dieselbe genehmigte Anfrage an dasselbe Ziel sendet, kann die Aktion innerhalb der Grenze bleiben. Wenn die Wiederherstellung Mandant, Ziel, Berechtigungsumfang oder für die Anwendung sichtbare Header verändert, handelt es sich um eine andere Anfrage, die eine neue Genehmigung braucht. Vermeide einen Hintergrundmechanismus, der einen Autorisierungsfehler in einen ungeprüften alternativen Aufruf verwandelt.

Das standardmäßige Verhalten bei HTTP-Weiterleitungen sollte für genehmigte Aktionen „ablehnen“ sein. Eine Weiterleitung, die jedes relevante Feld beibehält, kann trotzdem zu einer anderen Autorität führen. Wenn das Produkt Weiterleitungen unterstützt, prüfe jeden Sprung vor dem Versand und fordere ein neues Aktionsobjekt an, sobald sich Host, Port, Methode oder Body ändern. Behandle eine relative Weiterleitung auf derselben Autorität nach einer ausdrücklichen Regel und nicht nach dem zufälligen Verhalten der zugrunde liegenden Client-Bibliothek.

Für SSH-Verbindungen gilt eine ähnliche Regel. Verbinde dich nur erneut mit dem gebundenen Hostplan, der gebundenen Identität und dem gebundenen Befehl. Ein neu entdeckter Host-Fingerprint, eine geänderte Proxy-Route oder ein anderes Ergebnis der Hostauflösung kann abhängig von deinem Bedrohungsmodell eine neue menschliche Entscheidung erfordern. Der Wiederholungscode muss so unspektakulär sein, dass ein Prüfer erkennen kann, dass er die ursprüngliche Genehmigung nicht erweitern kann.

## Genehmigung und Ausführung als getrennte Ereignisse protokollieren

Ein Audit-Datensatz muss sowohl die von der Person geprüfte Aktion als auch die vom Executor unternommenen Versuche bewahren. Ein Log, das nur die letztendliche Anfrage aufzeichnet, kann nicht belegen, ob die Vorschau mit ihr übereinstimmte. Ein Log, das nur die Genehmigung aufzeichnet, kann nicht belegen, ob ein Versand stattfand.

Zeichne die ID der eingefrorenen Aktion, den Bindungs-Digest, die Identität des Aufrufers, vorschautaugliche Felder, die Kennung der Zugangsdaten-Referenz, Genehmigungszeit, Ablaufzeit und Ergebnis auf. Zeichne anschließend jeden Versandversuch unter derselben ID auf, einschließlich Transportergebnis, gegebenenfalls Wiederholungsgrund und Digest des tatsächlich gesendeten Bodys oder Befehls. Halte geheime Werte sowohl aus dem benutzerseitigen Journal als auch aus der Antwort des Agenten heraus.

Hier zeigt sich der Nutzen einer Hash-Kette. Eine Kette kann das Entfernen oder Umordnen früherer Einträge sichtbar machen, wenn die Prüfung die gespeicherte Reihenfolge umfasst. Sie kann aus einem vagen Ereignis kein hilfreiches Ereignis machen. Wenn der Datensatz nur „SSH-Aktion genehmigt“ sagt, bewahrt die Kette dieses vage Ereignis für immer auf.

Bei einem Gateway mit einem schreibgeschützten verschlüsselten Hash-Ketten-Log sollte ein Offline-Prüfer die Chiffretext-Kette unabhängig vom Tresorzugriff prüfen. Sallyport stellt diese Prüfung über `sp audit verify` bereit. Das ist nützlich, weil ein Ermittler die Kontinuität prüfen kann, ohne zuerst das Zugangsdaten-Tresor zu öffnen.

Die Audit-Ausgabe sollte die Bindung sichtbar machen. Ein Prüfer sollte sehen können, dass die Genehmigung `7c91e2d4` den Digest `4f06...` für `PATCH /v1/users/42` abdeckte, dass der Versand denselben Digest verwendete und dass der Executor jeden Vorschlag mit abweichenden Werten ablehnte. Vermeide Logs, die bei der Genehmigung eine menschliche Zusammenfassung und bei der Ausführung eine separate Rohanfrage ausgeben, ohne eine dauerhafte Verbindung zwischen beiden.

## Teste den Client so, als wolle er seine Meinung ändern

Ein normaler Integrationstest beweist, dass eine genehmigte Aktion erfolgreich ist. Ein Sicherheitstest muss beweisen, dass eine genehmigte Aktion nicht zu einer anderen werden kann, selbst wenn sich der Client nach dem Klick schlecht verhält.

Baue einen Testaufbau, der unmittelbar nach der Genehmigung pausiert. Verändere während des Wartens auf den Versand nacheinander jede vom Client kontrollierte Eingabe: Methode, Host, Port, Pfad, Query, Headerwerte, wiederholte Header, Body, Zugangsdaten-Auswahl, Weiterleitungseinstellung, SSH-Benutzer, Befehl, Umgebung, stdin und Datei-Digest. Der Executor sollte entweder die ursprüngliche gespeicherte Aktion ausführen oder den Versuch ablehnen. Die Veränderung darf niemals ausgeführt werden.

Teste Race-Conditions ebenso bewusst wie Feldänderungen. Sende zwei `execute`-Nachrichten für dieselbe genehmigte ID. Widerrufe die Genehmigung und sende gleichzeitig `execute`. Beende den anfordernden Prozess und verbinde danach einen neuen Prozess, der die alte ID verwendet. Starte die Benutzeroberfläche neu, während eine Genehmigung wartet. Fülle den Speicher für ausstehende Aktionen, bis die Bereinigung abgelaufener Einträge läuft. Das sind normale Lebenszyklusereignisse, und Autorisierungscode versagt oft an den Übergängen zwischen ihnen.

Verwende einen simulierten HTTP-Server, der die exakt empfangene Methode, das Ziel, doppelte Header und den Body-Digest meldet. Verwende einen kontrollierten SSH-Endpunkt, der Remote-Benutzer, Befehls-Bytes, Terminalanforderung, Umgebung und stdin-Digest aufzeichnet. Prüfe diese Beobachtungen und nicht nur das Absichtsobjekt des Gateways. Genau darum geht es: eine Abweichung zwischen Absicht und Transport zu erkennen.

Mache fehlgeschlagene Tests schließlich verständlich. Eine hilfreiche Fehlermeldung sagt, dass der Genehmigungs-Digest `4f06...` `PATCH /v1/users/42` abdeckte, während der Versandversuch einen anderen Digest enthielt und abgelehnt wurde. Eine vage Meldung wie „Autorisierung fehlgeschlagen“ schickt Entwickler zurück zu einfachen Print-Anweisungen und verleitet sie dazu, Prüfungen abzuschwächen, bis der Test grün wird.

## Die Unveränderlichkeitsgrenze neben dem Executor platzieren

Die Komponente, die Zugangsdaten hält und die HTTP- oder SSH-Verbindung öffnet, muss Eigentümer der eingefrorenen genehmigten Aktion sein. Jede frühere Grenze lässt einer weiteren Schicht die Möglichkeit, sie neu zu interpretieren, neu aufzubauen oder zu ersetzen.

Dieses Design hält auch die menschliche Interaktion ehrlich. Die Person sieht eine Vorschau, die aus dem zu sendenden Objekt erzeugt wurde. Der Agent erhält Ergebnisse, keine Zugangsdaten. Das Audit-Protokoll besitzt eine ID, die Absicht, Genehmigung und Ausführung verbindet. Das sind eigenständige Vorteile, aber alle hängen von derselben Weigerung ab, Änderungen nach dem Klick zu akzeptieren.

Wenn dein aktueller Ablauf ein Genehmigungstoken an den Agenten zurückgibt und danach neue Aktionsfelder akzeptiert, korrigiere das, bevor du bessere Texte, weitere Richtlinien oder einen zusätzlichen Genehmigungsdialog einführst. Friere die Anfrage zuerst ein. Lass den Executor anschließend nachweisen, dass er den eingefrorenen Datensatz verwendet hat.
