Replay-Angriffe gegen KI-Agenten: Prävention durch Design
Verhindern Sie Wiederholungsangriffe gegen KI-Agenten, indem Sie Unterschriften verpflichten, um Kontext, Mandate, Nonce-Status, Idempotenz und deterministische Verifizierungsreihenfolge anzufordern.

Ein Replay-Angriff gegen einen KI-Agenten kann eine vollkommen gültige signierte Anfrage wiederverwenden. Eine Signatur beantwortet eine engere Frage: Hat der Inhaber eines bestimmten Schlüssels diese Bytes signiert? Es sagt Ihnen nicht, ob diese Bytes die HTTP-Anfrage beschreiben, die Ihren Dienst erreicht hat. Es beweist nicht, dass das angehängte Mandat zur Transaktion gehört. Es verhindert nicht, dass dasselbe signierte Objekt zweimal verwendet wird.
Diese Lücke ist, wo mehrere hässliche Enterprise Agent Angriffe leben.
Stellen Sie sich einen Einkäufer vor, der 20 Ersatzlaufwerke von supplier-a.example für bis zu 4.000 EUR. Der Agent unterschreibt eine Anfrage. Ein Vermittler behält die Unterschrift und ändert das Zielkonto, tauscht das Mandat gegen ein breiteres aus oder wiederholt die gültige Anfrage nach dem ersten Auftrag. Jedes einzelne Objekt kann immer noch analysieren. Jede Unterschrift kann immer noch überprüfen. Die Transaktion ist immer noch falsch.
Der Prüfer muss Identität, Autorität, Anforderungskontext und veränderlichen Nutzungszustand in einer deterministischen Entscheidung binden.
Beginnen Sie mit der Replay Attack Surface
Verwenden Sie eine konkrete Anfrage:
POST /v1/purchase-orders HTTP/1.1
Host: supplier-a.example
Content-Type: application/json
OATI-Passport: <signed passport>
OATI-Mandate: <signed mandate>
OATI-Envelope: <signed envelope>
{
"sku": "drive-8tb-enterprise",
"quantity": 20,
"unit_price": "175.00",
"currency": "EUR",
"ship_to": "warehouse:berlin-02"
}
Das Mandat erlaubt purchase_order.create für diese SKU, Lieferant und Bestimmungsort. Es begrenzt den Stückpreis und das kumulative Budget. Der Umschlag benennt die Transaktion, die Zielgruppe und den Request Digest. Das sieht respektabel aus, aber jede ausgelassene Bindung eröffnet einen anderen Angriff.
Wiederholung
Der Angreifer sendet die genaue Anfrage zweimal. Die Signaturverifizierung gelingt beide Male, weil sich die Bytes nicht geändert haben. Ein Zeitstempel begrenzt nur das Wiedergabefenster.
Verwenden Sie zwei Replay-Steuerelemente mit verschiedenen Jobs:
- Eine Proof-Nonce verhindert, dass der gleiche signierte Proof zweimal für denselben Verifizierungsschlüssel und dieselbe Zielgruppe akzeptiert wird.
- Ein Business-Idempotenzschlüssel verhindert, dass dieselbe beabsichtigte Operation zweimal ausgeführt wird, auch wenn der Anrufer eine neue Signatur für einen Wiederholungsversuch erstellt.
Diese Schlüssel benötigen eine atomare Claim-Operation, ein Read gefolgt von einem Write ist anfällig für gleichzeitige Anfragen.
type ClaimResult = 'claimed' | 'already_claimed' | 'unavailable'
async function claimOnce(key: string, expiresAt: Date): Promise<ClaimResult> {
// Back this with SET NX, a conditional database insert, or equivalent.
// Never implement this as get(key), then set(key).
return replayStore.compareAndSetAbsent(key, expiresAt)
}
Anderenfalls kann eine Nonce-Kollision zwischen Emittenten oder Diensten falsche Denials erzeugen, während eine Nonce, die unter dem falschen Umfang aufgezeichnet wurde, eine Wiederverwendung ermöglichen kann.
const replayKey = [
proof.verification_method,
expectedAudience,
proof.nonce
].join('\u0000')
Für eine materielle Aktion, unavailable Die Behandlung eines defekten Replay-Stores als leeren Replay-Store verwandelt einen Ausfall in einen Autorisierungs-Bypass.
Kontext-Swap
Angenommen, der Agent unterschreibt einen Umschlag, der sagt transaction_id = txn-91Ein Angreifer kann die Menge von 20 auf 200 ändern, während der Umschlag intakt bleibt.
Das aktuelle HTTP-Bindungsprofil von OATI verwendet die Großbuchstabenmethode, Pfad plus Abfrage und einen SHA-256 Digest des rohen Request-Bodys:
POST\n
/v1/purchase-orders?region=eu\n
sha256:<raw-body-digest>
Andere Protokolle können einen Ursprung oder ausgewählte Header binden, aber das ist ein anderes Profil. Definieren Sie einmal die Normalisierung und Byte-Codierung, veröffentlichen Sie Testvektoren und identifizieren Sie das Profil im signierten Beweis. Die Audience-Verifizierung bindet den Beweis separat an den beabsichtigten Dienst.
const actualDigest = digestRequest({
method: request.method.toUpperCase(),
pathAndQuery: request.pathAndQuery,
rawBodyDigest: sha256(await request.rawBody())
})
if (!constantTimeEqual(actualDigest, envelope.request_digest)) {
return deny('REQUEST_DIGEST_MISMATCH')
}
Eine Anfrage, die für einen Testendpunkt, einen regionalen Dienst oder einen Lieferanten unterzeichnet wurde, darf nicht gegen einen anderen Endpunkt funktionieren, nur weil beide dem gleichen Emittenten vertrauen.
Destination Zwänge erfordern eine ähnliche Behandlung. ship_to Sie erscheint nur in einer nicht unterzeichneten Verlängerung, sie kann ersetzt werden, in einen unterzeichneten Kontext gestellt und mit den zulässigen Zielen des Mandats verglichen werden.
Mandatsersetzung
Nehmen wir nun an, dass Körper und Umschlag korrekt gebunden sind. Ein Angreifer fügt dem gleichen Agenten ein anderes, gültiges Mandat bei, der Ersatz hat ein höheres Budget oder einen anderen Lieferanten.
Der Umschlag muss das genaue Mandat identifizieren und vorzugsweise seinen kanonischen Digest binden. Der Prüfer überprüft dann, ob der Mandatssubjekt mit dem Passport-subjekt übereinstimmt und ob der Mandatsherausgeber für die verantwortliche Organisation akzeptiert wird.
{
"transaction_id": "urn:oati:txn:01K9A7...",
"agent_id": "oati:agent:buyer:procurement-7",
"mandate_id": "oati:mandate:buyer:po-8841",
"mandate_digest": "sha256:3a6f...",
"audience": "https://supplier-a.example",
"request_digest": "sha256:91e8...",
"issued_at": "2026-08-10T08:31:12Z",
"nonce": "8d250d63-6f91-49f8-a0f7-94f58f4b1831"
}
Wählen Sie keine Befugnis aus, indem Sie nach einem Mandat suchen, das eine Anfrage erfüllen kann. Überprüfen Sie das Mandat, das die Transaktion explizit benennt.
Die Delegation fügt einen weiteren Substitutionspfad hinzu. Ein Kinderagent kann ein legitimes Kindermandat vorlegen, während er seine Eltern versteckt. Die vollständige Delegationskette überprüfen und nachweisen, dass jedes Kind gleich oder schmaler als sein Elternteil ist. Fehlende Einschränkungen dürfen keine unbegrenzte Autorität bedeuten.
Für festgelegte Einschränkungen sind Teilmengenprüfungen einfach. Numerische Obergrenzen müssen sinken oder gleich bleiben. Der Ablauf kann nicht über das Mutterunternehmen hinausgehen. Die Delegationstiefe muss sinken. Zweck-, Ziel- und Gegenparteibeschränkungen müssen jeden Hopfen überstehen.
function assertChildSubset(parent: Mandate, child: Mandate) {
assert(isSubset(child.actions, parent.actions))
assert(isSubset(child.resources, parent.resources))
assert(isSubset(child.counterparties, parent.counterparties))
assert(isSubset(child.destinations, parent.destinations))
assert(child.limits.max_amount <= parent.limits.max_amount)
assert(child.expires_at <= parent.expires_at)
const remainingDepth = parent.delegation.max_depth - 1
assert(child.delegation.max_depth <= remainingDepth)
}
Echte Bewerter brauchen auch klare Semantik für Platzhalter, fehlende Felder und strukturierte Ressourcen. Ein String-Präfix-Vergleich für Ressourcen-Identifikatoren reicht selten aus.
Verifizierungsauftrag ist eine Wertpapiereigenschaft
Ein zuverlässiges Gateway führt Prüfungen in einer expliziten Reihenfolge durch:
- Erzwingen Sie Größenbeschränkungen, analysieren und validieren Sie dann Schemata.
- Lösen Sie Emittentenketten auf konfigurierte Vertrauensanker.
- Überprüfen Sie die Gültigkeit, den Status und den Widerruf der Schlüssel.
- Erstellen Sie kanonische signierende Nutzlasten und verifizieren Sie Signaturen.
- Überprüfen Sie Aktivierung, Ablauf, Nachweisalter und Publikum.
- Berechnen Sie den Anforderungsverdau aus der empfangenen HTTP-Anfrage und lehnen Sie eine Fehlanpassung ab.
- Behaupten Sie die Beweis-Nonce atomar.
- Match Agent, Passport, Mandat und Envelope Identifier.
- Überprüfen Sie die Delegationskette und die Nichtverstärkung.
- Bewerten Sie Aktion, Ressource, Zweck, Gegenpartei und Ziel.
- Reservebudget, Anrufzahl und einmalige Nutzung atomar.
- Führen Sie den vorgelagerten Vorgang aus und stellen Sie eine unterzeichnete Quittung aus.
Wenn Sie nur eine Nonce nach erfolgreicher Ausführung speichern, können gleichzeitige Anforderungen beide die Verifizierungsgrenze überschreiten. Zuerst lehnen Sie fehlerhafte Eingaben, ungültige Signaturen und anforderungsbindende Fehlanpassungen ab, damit ein Angreifer keine legitime Nonce mit einem veränderten Körper verbrennen kann. Sobald der authentische Beweis mit der empfangenen Anforderung übereinstimmt, fordern Sie seine Nonce an, bevor die Autorisierung fortgesetzt wird.
Nutzungsreservierung hat einen härteren Fehlermodus. Ein Gateway reserviert den letzten zulässigen Anruf, leitet die Anfrage weiter und verliert dann die Antwort. Das Freigeben der Reservierung kann eine doppelte Aktion ermöglichen. Es kann Autorität verbrauchen, selbst wenn der Upstream nie gehandelt hat.
Für Folgeschriften ist der sicherere Standard, die Reservierung in einem uncertain Die Verfügbarkeit verliert an Nicht-Duplizierung.
AUTHORIZED -> RESERVED -> SENT -> CONFIRMED
|
+-----> UNCERTAIN -> RECONCILED
Behandeln Sie Idempotenz und Wiederholung als separate Kontrollen
Der Replay-Schutz lehnt die Wiederverwendung eines kryptographischen Nachweises ab. Die Idempotenz gibt das Ergebnis eines Geschäftsvorgangs zurück oder rekonstruiert es, das der Kunde absichtlich versucht.
Ein Kunde kann ausdauern und den gleichen Kauf mit einer neuen Beweis-Nonce wiederholen. Der Wiederholungs-Check sollte bestehen, weil dies ein neuer Beweis ist. Die Idempotenzschicht sollte den Geschäftsschlüssel erkennen und das bestehende Auftragsergebnis zurückgeben, anstatt einen anderen Auftrag zu erstellen.
Wenn der gleiche Schlüssel mit einem geänderten Betrag oder Ziel ankommt, geben Sie einen Konflikt zurück, nicht das frühere Ergebnis.
INSERT INTO operation_claims (
tenant_id, idempotency_key, request_digest, state
) VALUES ($1, $2, $3, 'reserved')
ON CONFLICT (tenant_id, idempotency_key) DO NOTHING;
Dann vergleichen request_digest Ein Idempotenz-Schlüssel ist keine allgemeine Erlaubnis, einen ausstehenden Befehl zu ändern.
Bauen Sie eine Angriffsvorrichtung, nicht nur einen Happy-Path-Test
Eine signierte Transaktion kann eine nützliche kontradiktorische Suite generieren:
| Mutation | Erwartetes Ergebnis |
|---|---|
| Zweimal identische Beweise einreichen | REPLAY_DETECTED |
| Frischer Beweis, gleicher Business Key und Body | Bestehendes Ergebnis oder ausstehender Status |
| Gleicher Business Key, geänderter Betrag | Idempotenzkonflikt |
| HTTP-Methode oder Ziel ändern | Antrag auf Digest Dismatch |
| Körper Whitespace nur ändern | gleiche Verdauung, wenn kanonische JSON definiert ist |
| Änderungsmenge oder Bestimmungsort | Antrag auf Digest Dismatch |
| Präsentierter Umschlag für ein anderes Publikum | Nichtübereinstimmung der Zuschauer |
| Ein anderes gültiges Mandat beifügen | Mandatskennung oder Digest-Inkongruenz |
| Elterneinschränkung beim Kind entfernen | Nichtverstärkungsfehler |
| Mandat nach Widerruf verwenden | widerrufene Zielverweigerung |
| Rennen 64 Kopien von einer Nonce | Genau ein Beweisanspruch gelingt |
| Replay Storage deaktivieren | Materialaktion scheitert geschlossen |
Doppelte JSON-Schlüssel, Unicode-Normalisierung, extreme Zahlen und mehrdeutige URLs verursachen seit Jahren Probleme mit Signatursystemen. Eine Konformitätssuite sollte dazu führen, dass jede Implementierung die gleichen Bytes und Grundcodes erzeugt.
Verwandte technische Anleitungen
- Setzen Sie Prävention auf die MCP-Autorisierungspfad.
- Wenden Sie die gleiche Nonce- und Idempotenztrennung an Automatisierung von Zahlungskonten.
Was OATI heute unterstützt
Die OATI Entwicklervorschau umfasst RFC 8785 / JCS Canonicalization, Ed25519 und ES256 Verifizierungsprofile, Audience- und Replay-Checks, zielspezifische Widerrufe, deterministisch Mandat Auswertung, Delegations-Subset-Proofs, Request-Digest-Bindung und gemeinsam genutzte Konformitätsvektoren. Die TypeScript-Referenz-Middleware erfordert standardmäßig Request-Bindung. Python und Go implementieren den tragbaren Kern gegen dieselbe sprachneutrale Suite.
Das ist nützliches Implementierungsmaterial, aber es ist kein Anspruch auf abgeschlossene Produktionssicherheit. Unabhängige kryptographische und Protokollüberprüfung bleibt offen. Produktionsausfallübungen, verlängerte Rennen und Streittests und der dauerhafte Beweis- und Streitdienst sind noch keine vollständigen Release-Gates.
Checkliste der Durchführung
- Definieren Sie eine kanonische Darstellung für jedes signierte Objekt und jede HTTP-Anfrage.
- Binden Sie die HTTP-Methode, Pfad plus Abfrage und Rohkörperverdau entsprechend dem ausgewählten Profil; überprüfen Sie das Publikum separat.
- Binden Sie die genaue Reisepass-, Mandats- und Delegationskette an den Umschlag.
- Lösen Sie den aktuellen Schlüssel und den Widerrufszustand vor der Autorisierung.
- Verwenden Sie einen atomaren Wiederholungsanspruch, der nach Schlüssel, Publikum und Nonce festgelegt ist.
- Halten Sie die Wiederholungs-Nonce- und Business-Idempotenz-Semantik getrennt.
- Speichern Sie einen Operation Fingerabdruck mit jedem idempotency Schlüssel.
- Machen Sie Kinderautorität zu einer deterministischen Untergruppe der Elternautorität.
- Reservebudgets und einmalige Nutzung mit Vergleichs- und Set-Semantik.
- Ungewisse Hinrichtungen bleiben bis zur Abstimmung vorbehalten.
- Fail closed, wenn Wiederholungs-, Vertrauens- oder Nutzungsstatus für materielle Aktionen nicht verfügbar ist.
- Testmutationen, Rassen, abgestandene Caches und Teilausfälle.
- Unterschreiben Sie eine Quittung, die den Anforderungsverdau, die Richtlinienentscheidung und das Ausführungsergebnis bindet.
Ein Verifier benötigt den signierten Anforderungskontext, die beabsichtigte Zielgruppe, die genaue Autorität und den aktuellen Nutzungszustand, bevor er die Transaktion autorisieren kann.