# Content-Type-Verwechslung bei authentifizierten Agent-API-Aufrufen

Authentifizierte Agent-Aufrufe müssen vom Netzwerk-Edge bis zum Aktions-Handler einheitlich interpretiert werden. Wenn das Gateway eine harmlose JSON-Anfrage sieht, die Autorisierungsschicht einen Satz von Feldern erhält und der Handler eine privilegierte Formularübermittlung verarbeitet, hat das Zugangsmittel funktioniert und die API ist trotzdem unsicher.

Das ist kein Randthema, das nur alte Browserformulare betrifft. Agents erzeugen HTTP direkt, wiederholen Anfragen sehr aggressiv, übernehmen häufig Beispiele aus Tool-Beschreibungen und arbeiten oft mit Zugangsdaten, die reale Systeme verändern können. Ein Anfrage-Body gehört immer dann zur Autorisierungsentscheidung, wenn er ein Ziel, einen Betrag, eine Umgebung, einen Befehl oder eine Berechtigung auswählt. Medientyp, Syntax und Schema müssen eindeutig sein, bevor du entscheidest, ob der Aufrufer handeln darf.

## Eine authentifizierte Anfrage muss trotzdem genau eine Bedeutung haben

Authentifizierung beantwortet die Frage, wer ein Zugangsmittel vorgelegt hat. Autorisierung beantwortet, ob dieser Aufrufer eine Aktion ausführen darf. Keine der beiden Antworten sagt dir, ob alle Komponenten sich auf dieselben Argumente der Aktion geeinigt haben.

Betrachte einen Endpoint, der ein Deployment-Ziel ändert:

```http
POST /v1/deployments/promote HTTP/1.1
Authorization: Bearer <token>
Content-Type: application/json

{"environment":"staging","release":"2026.07.22"}
```

Der Autorisierungscode kann eine Beförderung nach `staging` erlauben, aber Produktion ablehnen. Das ist nur dann sicher, wenn er denselben Wert für `environment` erhält, den auch der Aktions-Handler verwendet. Liest eine Middleware JSON, greift der Handler später auf Formularparameter zu und dürfen beide dieselbe Anfrageobjekt-Struktur befüllen, hast du zwei Quellen der Wahrheit geschaffen.

Dafür ist kein fehlerhaftes kryptografisches Token nötig. Ein Agent mit einer legitimen Sitzung kann einen Body senden, den eine Schicht ignoriert und eine andere berücksichtigt. Ein kompromittierter Agent kann dasselbe tun. Das Ergebnis ist eine Umgehung der Autorisierung, die sich als Eingabeformat äußert.

RFC 9110 sagt, dass `Content-Type` den Medientyp der zugehörigen Darstellung angibt und sowohl das Datenformat als auch die vorgesehene Verarbeitung durch den Empfänger beschreibt. Der Header gehört damit zur Bedeutung der Anfrage und ist keine Dekoration. Derselbe RFC erlaubt einem Empfänger ohne `Content-Type`, `octet-stream` anzunehmen oder die Daten zu untersuchen. Das ist für allgemeine Dateiverarbeitung nützlich, aber ein schlechter Standard für geschützte Aktions-APIs.

Lege für einen Aktions-Endpoint diese Invariante fest:

> Genau ein akzeptierter Medientyp ordnet die Anfrage-Bytes genau einem validierten Befehlsobjekt zu. Jede Sicherheitsentscheidung und jeder Seiteneffekt verwendet dieses Objekt.

Der Endpoint kann mehr als eine Darstellung unterstützen. Jede Darstellung braucht dann aber ihren eigenen Vertrag und ihre eigene Testsuite. Behandle mehrere Parser nicht als austauschbare Bequemlichkeit.

## Ein Content-Type-Header ist kein Schema

`Content-Type: application/json` bedeutet nicht: „Das ist die erwartete Form der Anfrage.“ Der Header behauptet nur, dass der Body einen JSON-Medientyp verwendet. Du musst weiterhin entscheiden, ob dieser Typ für die Route unterstützt wird, ob Parameter erlaubt sind, ob der Body syntaktisch gültig ist und ob der dekodierte Wert zum Vertrag der Operation passt.

Ein geschützter Endpoint sollte die erlaubte Menge an Darstellungen bewusst klein halten. Viele Command-Endpoints sollten ausschließlich JSON akzeptieren. Ein Upload-Endpoint kann ausschließlich Multipart akzeptieren. Eine Aktion ohne Argumente sollte überhaupt keinen Body annehmen. Je größer die akzeptierte Menge, desto mehr Parserpfade musst du beherrschen.

Das OWASP REST Security Cheat Sheet gibt eine klare praktische Empfehlung: Unterstützte Content-Types dokumentieren und unerwartete oder fehlende Typen ablehnen. Ein fehlender Content-Type kann für eine Anfrage mit einer Länge von null Bytes erlaubt werden. Außerdem müssen Body und deklarierter Typ zusammenpassen, damit Produzent und Empfänger die Daten nicht unterschiedlich interpretieren.

Für authentifizierte Aktionen braucht diese Empfehlung eine Ergänzung. Gleiche einen behaupteten Typ nicht ab, indem du das erste Zeichen untersuchst und danach einen Parser auswählst. Ein Body, der mit `{` beginnt, ist keine Erlaubnis, eine als Formulardaten deklarierte Anfrage als JSON zu verarbeiten. Das Erraten des Typs macht aus einem klaren Vertrag eine Implementierungsvermutung.

Ein sinnvoller Routenvertrag sieht so aus:

| Route | Erlaubter Anfrage-Medientyp | Body-Regel |
|---|---|---|
| `POST /v1/deployments/promote` | `application/json` | Erforderliches JSON-Objekt passend zu `PromoteRequest` |
| `POST /v1/artifacts` | `multipart/form-data` | Erforderliche Teile passend zu `ArtifactUpload` |
| `POST /v1/sessions/revoke` | keiner | Muss null Bytes enthalten |

Sei bei Parametern des Medientyps genau. Wenn dein JSON-Parser `application/json; charset=utf-8` akzeptiert, dokumentiere das und normalisiere Parameter über eine einzige Bibliothek. Akzeptiert er nur das reine `application/json`, lehne den Parameter ab, statt Proxy und Anwendung unterschiedliche Regeln verwenden zu lassen. Welche Variante du wählst, ist weniger wichtig als ihre einheitliche Anwendung.

Trenne außerdem die Antwortpräferenz `Accept` vom `Content-Type` der Anfrage. Ein Client kann eine JSON-Antwort anfordern und gleichzeitig einen ungültigen Anfrage-Body senden. Lass den `Accept`-Header niemals erweitern, welche Anfrageformate ein Aktions-Endpoint verarbeitet.

## JSON braucht mehr Regeln als gültige Syntax

Ein JSON-Parser kann Eingaben erfolgreich verarbeiten, die deine API trotzdem ablehnen muss. Doppelte Elementnamen sind das offensichtliche Beispiel:

```json
{"environment":"staging","environment":"production","release":"2026.07.22"}
```

RFC 8259 sagt, dass Objektnamen eindeutig sein sollten, und erklärt auch den Grund: Empfänger gehen mit doppelten Namen nicht einheitlich um. Viele behalten den letzten Wert, manche brechen ab und andere geben jedes Paar aus. Das ist ein dokumentiertes Interoperabilitätsproblem, keine theoretische Stilfrage.

Angenommen, eine Autorisierungs-Middleware verwendet einen Parser, der den ersten Wert für `environment` behält, während ein nachgelagerter Decoder den letzten verwendet. Die Middleware genehmigt `staging`, der Handler befördert aber nach Produktion. Das lässt sich nicht mit besseren Rollennamen oder einem weiteren Token-Claim reparieren. Lehne die Anfrage ab, bevor eine der beiden Komponenten eine Entscheidung trifft.

Führe dieselbe Prüfung für Werte durch, die in einer lockeren Sprachbindung harmlos aussehen:

- Lehne unbekannte Objektfelder bei Aktionsanfragen ab, sofern es keinen ausdrücklich begründeten Kompatibilitätsgrund gibt, sie zu behalten.
- Verlange den erwarteten JSON-Typ. Ein Boolean ist kein String, der zufällig `true` sagt, und eine ganzzahlige ID ist keine Gleitkommazahl.
- Begrenze die Body-Größe vor dem Parsen. Ein Schema-Validator schützt keinen Speicher, den du beim Einlesen eines riesigen Bodys bereits erschöpft hast.
- Lege fest, ob ein Feld fehlen, `null` oder ein leerer String sein darf. Das sind drei verschiedene Zustände.
- Lehne nachfolgende Daten und Parser-Erweiterungen wie Kommentare, `NaN` oder nicht quotierte Namen ab, sofern deine Bibliothek sie anbietet.

Autorisiere nicht direkt aus einer generischen Map. Dekodiere in einen Request-Typ mit explizitem Schema, führe semantische Validierung durch und erstelle anschließend einen internen Befehlstyp, der keine rohen Parser-Artefakte behält. Ein Handler, der `PromoteCommand { environment, release }` erhält, kann Eingaben schwerer neu interpretieren als ein Handler, der eine Map, eine Query-Sammlung, ein Request-Objekt und den rohen Body erhält.

Bei Zahlen ist besondere Sorgfalt nötig. Die JSON-Grammatik erlaubt große numerische Literale, aber viele Laufzeitumgebungen dekodieren Zahlen standardmäßig als Gleitkommadarstellung. Wenn ein Wert Geld, Quoten, Datenbankeinträge oder eine signierte Nutzlast bezeichnet, verwende ein String-Format oder einen Integer-Parser mit dokumentiertem Wertebereich. Lass nicht zu, dass eine Schicht eine Zahl rundet, bevor eine andere sie vergleicht.

## Formular-Bodies erzeugen versteckte Regeln für Arrays und Verschachtelung

`application/x-www-form-urlencoded` wirkt einfach, weil es einer Query-Zeichenfolge ähnelt. Es ist nicht mehr einfach, sobald Bibliotheken wiederholten Namen, Klammernotation, Pluszeichen und leeren Werten eine Bedeutung geben.

Betrachte diese Bodies:

```text
role=user&role=admin
role[]=user&role[]=admin
role[user]=1&role[admin]=1
role=user%26role%3Dadmin
```

Unterschiedliche Frameworks können daraus einen letzten oder ersten Skalar, ein Array, ein Objekt, wörtliche Feldnamen oder einen Parserfehler machen. Manche Middleware parst Formulare für jede HTTP-Methode. Manche Anwendungsframeworks führen Query- und Formularparameter in einem Komfortobjekt zusammen. Genau dort verlieren geschützte APIs den Überblick darüber, was der Aufrufer tatsächlich gesendet hat.

Die Testempfehlungen von OWASP zu HTTP-Parameter-Pollution weisen darauf hin, dass das Verhalten vom Zusammenspiel aus Anwendung, Webserver, WAF und Middleware abhängt. Genau deshalb solltest du rohe wiederholte Parameter testen, statt dich auf die Parserdokumentation eines einzelnen Frameworks zu verlassen.

Die verbreitete Empfehlung, aus Gründen der „Client-Kompatibilität“ jeden Endpoint für JSON und URL-kodierte Formulare zu öffnen, ist meistens falsch. Sie hält sich, weil sie das Schreiben eines Demo-Clients erleichtert und viele Frameworks sie standardmäßig aktivieren. Gleichzeitig verdoppelt sie die Darstellungsverträge für jede Aktion und fügt stillschweigend einen dritten Vertrag hinzu, sobald Query-Felder mit dem Body zusammengeführt werden.

Wenn du einen Formular-Endpoint unterstützen musst, gib ihm eine eigene Parser-Richtlinie:

1. Lehne wiederholte Namen ab, sofern das Schema das Feld nicht als Liste definiert.
2. Lehne Klammer-Syntax ab, sofern das Schema nicht deren exakte Kodierung definiert und dein Parser sie konsistent implementiert.
3. Halte Query-Parameter und Formularfelder getrennt. Keine Quelle darf die andere überschreiben.
4. Wandle die geparsten Felder erst nach der Validierung in denselben typisierten internen Befehl um, den auch die JSON-Route verwendet.
5. Teste Prozentkodierung, `+` im Vergleich zu `%20`, leere Werte, fehlendes `=` und doppelte Felder über den produktiven Anfragepfad.

Löse das nicht mit der Auswahl „first wins“ oder „last wins“. Das erzeugt zwar innerhalb einer Komponente eine deterministische Antwort, erhält aber die Uneinigkeit an anderer Stelle. Ein geschütztes skalares Feld sollte genau einmal vorkommen.

## Multipart ist ein Upload-Protokoll, kein flexibles JSON

`multipart/form-data` hat eine legitime Aufgabe: Es überträgt mehrere unabhängig mit Headern versehene Teile, oft mit Dateiinhalt. RFC 7578 definiert es für Formularwerte und verlangt einen Boundary-Parameter, der die Teile trennt. Jeder Teil kann außerdem eigene Header und Dateinamen-Metadaten enthalten.

Diese Struktur macht Multipart zu einer schlechten Fallback-Darstellung für gewöhnliche authentifizierte Befehle. Sie bringt mehr Syntax, mehr Anforderungen an die Größenverwaltung, mehr Stellen für doppelte Feldnamen und mehr Möglichkeiten mit, dass ein Gateway einen Teil prüft, während die Anwendung einen anderen auswählt.

Ein häufig schlechtes Design akzeptiert neben einer Datei einen JSON-Teil `metadata` und zusätzlich Formularfelder auf oberster Ebene, die die Metadaten überschreiben können:

```text
Content-Disposition: form-data; name="metadata"

{"project":"alpha","visibility":"private"}

Content-Disposition: form-data; name="visibility"

public
```

Eine Komponente kann anhand von `metadata.visibility` autorisieren. Eine andere bindet den späteren Formularteil an den Parameter `visibility` des Handlers. Die Anfrage enthält dann zwei Werte für eine sicherheitsrelevante Eigenschaft, ausgedrückt in zwei Grammatiken.

Entwirf Multipart-Endpoints mit benannten Teilen und klar getrennten Aufgaben. Akzeptiere beispielsweise genau einen Teil `file` und genau einen Teil `manifest`. Verlange, dass `manifest` JSON mit einem eigenen strikten Schema enthält. Lehne jeden Teilnamen ab, der nicht im Upload-Vertrag aufgeführt ist, lehne doppelte Einzelteile ab, setze getrennte Grenzen für die gesamte Body-Größe und die Dateigröße und lege fest, ob Content-Types auf Teilebene erforderlich sind.

Vertraue einem Dateinamen nicht als Pfad, einem MIME-Claim nicht als Dateiklassifizierung und dem Verhalten temporärer Dateien eines Multipart-Parsers nicht als Sicherheitskontrolle. Das sind eigenständige Upload-Probleme. Die Regel für Parser-Verwechslungen bleibt einfacher: Autorisierungseingaben müssen aus einer einzigen benannten und validierten Quelle stammen. Wenn `manifest.project` bestimmt, wo eine Datei landet, darf kein anderer Teil, kein Query-Parameter und kein Header dieses Projekt ändern.

Wenn ein Befehl keine Datei enthält, akzeptiere kein Multipart. Jeder zusätzliche akzeptierte Medientyp ist eine zusätzliche Möglichkeit, dass zwei Komponenten sich nicht einig sind.

## Ein leerer Body ist ein Vertrag, nicht das Fehlen einer Validierung

Manche authentifizierten Aktionen benötigen keine Argumente. Das Widerrufen der aktuellen Sitzung, das Rotieren einer serverseitig erzeugten Nonce oder das Bestätigen eines festen Ereignisses kann einen leeren Anfrage-Body verwenden. In solchen Fällen muss die Leere durchsetzbar sein.

Ein Endpoint ohne Body sollte alle folgenden Anfragen ablehnen:

```http
POST /v1/sessions/revoke HTTP/1.1
Content-Type: application/json
Content-Length: 2

{}
```

```http
POST /v1/sessions/revoke HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 11

scope=other
```

```http
POST /v1/sessions/revoke HTTP/1.1
Transfer-Encoding: chunked

0

```

Das letzte Beispiel enthält keinen Inhalt, verwendet aber weiterhin einen Framing-Mechanismus, den dein Vertrag ohne Body möglicherweise verbieten soll. Ob du es ablehnst, hängt von deinem HTTP-Stack ab. Entscheide das am Edge und teste es dort. Lass keinen Proxy ein Framing durchreichen, das die Anwendung anders behandelt.

Für eine Route ohne Body gelten vor der Geschäftslogik diese Regeln:

- Die Anfrage enthält keine Content-Bytes.
- Die Route akzeptiert keinen `Content-Type`, außer eine ausdrücklich dokumentierte Kompatibilitätsregel erlaubt ihn.
- Die Route führt Query-Parameter nicht in den Befehl zusammen, sofern nicht jeder erlaubte Query-Name ein eigenes Schema besitzt.
- Der Server zeichnet die Aktion als argumentfrei auf, statt ein generisches Request-Objekt zu protokollieren, das spätere Leser fälschlich für Eingabe halten könnten.

RFC 9110 beschreibt Anfrageinhalte anhand der Semantik der Methode. Ein Body erhält nicht allein deshalb eine universelle Bedeutung, weil die Anfrage POST verwendet. Diese Bedeutung liefert dein Ressourcenvertrag.

Der schwierige Fall ist eine Client-Bibliothek, die immer `{}` sendet. Erweitere den Endpoint nicht nur deshalb. Repariere den Client oder gib ihm eine separate, dokumentierte Route. Ein Body, der derzeit keine Wirkung hat, wird durch eine spätere Änderung am Handler leicht zu einem unbeabsichtigten Eingabekanal.

## Vor der Autorisierung validieren und den validierten Befehl ausführen

Die sicherste Anfragepipeline verläuft in eine Richtung. Rohe Bytes kommen hinein. Eine Route wählt genau einen erlaubten Parser. Der Parser erzeugt einen typisierten Wert. Die Validierung erzeugt einen kanonischen Befehl. Die Autorisierung bewertet diesen Befehl. Der Executor erhält denselben Befehl.

```text
raw HTTP request
  -> route and media-type check
  -> bounded body read
  -> one strict parser
  -> schema and semantic validation
  -> canonical command
  -> authorization
  -> execution and audit record
```

Vertausche die mittleren beiden Schritte nicht. Die Autorisierung braucht oft Felder wie Projekt-ID, Umgebung, Empfänger oder Befehlsmodus. Deshalb sind Teams versucht, frühzeitig lose geparste Eingaben zu prüfen. Dadurch entsteht ein Parser vor der Autorisierung, dessen Verhalten für immer mit dem finalen Decoder übereinstimmen müsste. Nur wenige Systeme halten dieses Versprechen.

Der kanonische Befehl ist eine praktische Grenze, kein Diagrammmuster. Er sollte nur die Werte enthalten, die der Executor benötigt, und rohen Body-Text, Formularsammlungen, Framework-Request-Objekte und Aliase ausschließen. Wenn dein Executor `target_environment` erhält, darf er später nicht `req.query.environment` heranziehen, nur weil das Ziel fehlte oder unbequem war.

Das verbessert auch Audit-Aufzeichnungen. Protokolliere den authentifizierten Principal, Endpoint, akzeptierten Medientyp, einen Request-Digest, die sicher speicherbaren Felder des kanonischen Befehls, die Autorisierungsentscheidung und das Ergebnis. Das standardmäßige Protokollieren roher Request-Bodies schafft ein zweites Problem, weil Bodies Zugangsdaten, hochgeladene Dateien und Benutzerdaten enthalten können. Ein Digest ermöglicht die Zuordnung zu gesicherten Belegen, ohne Logs in einen Geheimnisspeicher zu verwandeln.

Für das Signieren von Anfragen gilt dieselbe Disziplin. Wenn ein Client Bytes signiert, der Server aber ein normalisiertes Objekt autorisiert, müssen sowohl die Regeln für die signierte Darstellung als auch die Regeln für die Kanonisierung aufgezeichnet werden. Wenn der Client ein kanonisches Objekt signiert, lehne alle alternativen Kodierungen ab, bevor du die Signatur prüfst. Andernfalls können zwei Bytefolgen dieselbe Geschäftsanforderung tragen oder eine Folge nach dem Parsen eine andere Bedeutung erhalten.

## Teste die Uneinigkeit, nicht nur den erfolgreichen Parser

Unit-Tests, die ein gültiges JSON-Fixture deserialisieren, beweisen fast nichts über die Übereinstimmung von Parsern. Dein Testziel ist der öffentliche Anfragepfad: Load Balancer oder Reverse Proxy, Gateway, Framework-Middleware, Routen-Handler und jeder Service, der den Body erneut parst.

Erstelle für jede authentifizierte Operation eine kompakte Sammlung negativer Fälle. Sie sollte in CI gegen eine kurzlebige Umgebung laufen und sowohl die Antwort als auch das Ausbleiben von Seiteneffekten prüfen. Eine `400`-Antwort reicht nicht, wenn bereits eine Queue-Nachricht, ein Audit-Ereignis oder ein teilweiser Dateischreibvorgang entstanden ist.

Beginne mit diesem Shell-Harness. Es sendet absichtlich rohe Bodies, statt sich auf einen generierten Client zu verlassen, der fehlerhafte Eingaben ablehnt:

```bash
base=https://api.test.example
bearer='test-token'

send() {
  name=$1
  type=$2
  body=$3
  code=$(curl -sS -o "/tmp/${name}.out" -w '%{http_code}' \
    -X POST "$base/v1/deployments/promote" \
    -H "Authorization: Bearer $bearer" \
    -H "Content-Type: $type" \
    --data-binary "$body")
  printf '%-28s %s\n' "$name" "$code"
}

send valid_json 'application/json' \
  '{"environment":"staging","release":"2026.07.22"}'
send duplicate_json 'application/json' \
  '{"environment":"staging","environment":"production","release":"2026.07.22"}'
send form_body 'application/x-www-form-urlencoded' \
  'environment=production&release=2026.07.22'
send false_json 'application/json' \
  'environment=production&release=2026.07.22'
```

Die erwartete Form der Ausgabe sollte aus einem Erfolg und drei Ablehnungen durch den Client bestehen:

```text
valid_json                   200
 duplicate_json               400
form_body                    415
false_json                   400
```

Deine tatsächliche Statuskonvention kann für eine syntaktisch gültige Anfrage, die am Schema scheitert, `422` zurückgeben. Wichtig ist die Unterscheidung: Ein Medientyp-Konflikt darf nie einen Fallback-Parser erreichen, und ein doppeltes JSON-Element darf nie die Autorisierung erreichen.

Erweitere die Sammlung um Fälle, die Grenzen zwischen Komponenten testen:

| Fall | Erwartetes Verhalten |
|---|---|
| Fehlender `Content-Type` bei nicht leerem Body | Vor dem Parsen ablehnen |
| JSON-Objekt mit unbekanntem Feld | Ablehnen oder dokumentiertes Kompatibilitätsverhalten protokollieren |
| Wiederholter skalarer Formularwert | Ablehnen |
| Query-Wert widerspricht JSON-Wert | Ablehnen oder Query gemäß Routenvertrag ignorieren |
| Multipart enthält zwei `manifest`-Teile | Ablehnen |
| Route ohne Body erhält `{}` | Ablehnen |

Prüfe anschließend die Audit-Spur. Jede abgelehnte Eingabe sollte eine Spur hinterlassen, die Route und Ablehnungsklasse nennt, ohne vertrauliche Anfrageinhalte zu protokollieren. Jede akzeptierte Eingabe sollte genau einen kanonischen Befehl erzeugen. Wenn die Logs zeigen, dass das Gateway ein Ziel sah und der Handler ein anderes aufzeichnete, hast du eine Uneinigkeit gefunden, selbst wenn der Test eine 2xx-Antwort erhalten hat.

## Proxies und Middleware sind ebenfalls Parser

Teams zeigen oft auf den Parser der Anwendung und vergessen die Komponenten davor. Reverse Proxies können Header normalisieren. API-Gateways können JSON untersuchen, um eine Regel anzuwenden. WAFs können Formulardaten parsen. Observability-Middleware kann einen Body lesen und neu zusammensetzen. Ein Framework kann Query-, Formular- und JSON-Felder befüllen, bevor der Routen-Handler läuft.

OWASPs Hinweise zu HTTP-Request-Smuggling beschreiben die größere Variante dieses Problems: Intermediäre und Backend-Server können Anfragegrenzen unterschiedlich interpretieren, besonders bei Protokollübersetzung und Framing. Content-Type-Verwechslung braucht kein Request-Smuggling, um gefährlich zu sein. Beide Fehler entstehen jedoch dadurch, dass verschiedene Schichten inkompatible Parsing-Entscheidungen treffen dürfen.

Erfasse jeden Body-Leser im Aktionspfad. Notiere für jeden, welche Medientypen er parst, ob er doppelte Werte behält, ob er Inhalte dekomprimiert, ob er eine Größenbegrenzung durchsetzt und ob er den Body umschreiben kann. Wenn niemand diese Fragen beantworten kann, ist der Endpoint noch nicht für Agent-Zugangsdaten bereit.

Halte die Rolle des Gateways eng. Es kann Routen-Level-Bodysizes erzwingen und Medientypen blockieren, die eine Route niemals akzeptiert. Es kann auch fehlerhafte Header vor der Anwendung ablehnen. Verwende eine Gateway-Transformation jedoch nicht, um Formulardaten in JSON umzuwandeln oder doppelte Felder zu „bereinigen“. Die Anwendung muss Mehrdeutigkeit anhand genau der Semantik ablehnen, die sie ausführt.

Teste die HTTP-Versionen und Deployment-Pfade, die in der Produktion tatsächlich verwendet werden. Eine Anfrage, die sich gegen einen lokalen Entwicklungsserver korrekt verhält, kann sich ändern, wenn ein HTTP/2-Client einen Proxy erreicht, der HTTP/1.1 an die Anwendung weiterleitet. Es geht nicht darum, ein Forschungslabor für Angriffe aufzubauen. Die Produktionskette muss nachweisen, dass sie für jede akzeptierte Anfrage genau ein Befehlsobjekt erzeugt.

## Agent-Gateways müssen diese Grenze bewahren

Ein Agent-Gateway sollte Zugangsdaten vom Modell fernhalten und eine Aufzeichnung der Aktion bewahren. Es kann eine großzügige Ziel-API aber nicht allein sicher machen. Das Gateway muss eine Darstellung senden, die die Zielroute ausdrücklich unterstützt, und das Ziel muss diese Darstellung validieren, bevor es die Berechtigung bewertet.

Sallyports HTTP-Kanal fügt Zugangsdaten ein, während API-Schlüssel außerhalb des Agents bleiben. So kann ein Agent eine Aktion anfordern, ohne das Geheimnis selbst zu erhalten. Das ist eine nützliche Grenze für Zugangsdaten. Kombiniere sie mit Endpoint-Verträgen, die mehrdeutige Bodies ablehnen, denn geschützte Zugangsdaten autorisieren weiterhin die Anfrage, die die API erreicht.

Gib Agents Tools, die den Vertrag abbilden, statt für sensible Systeme eine generische Aktion „beliebige HTTP-Anfrage senden“ bereitzustellen. Ein Promotion-Tool sollte typisierte Argumente `environment` und `release` akzeptieren. Seine Implementierung sollte ein JSON-Objekt, einen Medientyp und nichts anderes serialisieren und Tool-Eingaben ablehnen, die das API-Schema nicht erfüllen können. Der empfangende Service muss die Validierung wiederholen. Tool-Schemas reduzieren Fehler, ersetzen aber nicht das Misstrauen auf der Serverseite.

Wenn ein Agent einen Upload benötigt, mache daraus ein eigenes Tool mit einer benannten Datei und einem benannten Manifest. Wenn er eine Aktion ohne Argumente braucht, biete überhaupt kein Body-Feld an. Diese kleinen Einschränkungen machen die beabsichtigte Anfrage leichter prüfbar, genehmigbar, in einer Testumgebung wiederholbar und später auditierbar.

Genehmige keine vage Fähigkeit und erwarte dann, dass Parser die fehlende Präzision liefern. Sorge dafür, dass der Endpoint genau eine Bedeutung akzeptiert, dass der Agent diese Bedeutung sendet und dass jede alternative Schreibweise abgelehnt wird, bevor ein Zugangsmittel sie autorisieren kann.
