8 Min. Lesezeit

Timing-Seitenkanaltests sollten Ablehnungen als Ergebnis behandeln

Timing-Seitenkanaltests helfen zu verhindern, dass Agenten den Tresorzustand anhand von Erfolg, Ablehnung, fehlenden oder ungültigen Zugangsdaten ableiten.

Timing-Seitenkanaltests sollten Ablehnungen als Ergebnis behandeln

Ein Agent braucht keinen geheimen Wert, um etwas Gefährliches zu lernen. Wenn er wiederholt ein Aktions-Gateway aufrufen und Ergebnisse nach ihrer Dauer sortieren kann, erfährt er möglicherweise, ob der Tresor gesperrt ist, ob ein Name für Zugangsdaten existiert, ob eine Anfrage autorisiert wurde oder ob das Gateway einen entfernten Dienst erreicht hat.

Diese Informationen reichen aus, um sein Verhalten zu ändern. Ein Coding-Agent, der erfährt, dass Zugangsdaten vorhanden sind, kann immer wieder Zugriff auf diesen Dienst anfordern. Ein kompromittierter Agent kann das Ergebnis zur Aufklärung nutzen. Bei einem einzelnen Aufruf wirkt das Leck vielleicht harmlos. Es wird nützlich, wenn der Aufrufer die Eingaben kontrolliert, Anfragen wiederholt und eine Uhr präzise messen kann.

MITRE führt diese Klasse als CWE-208, Observable Timing Discrepancy. Die Beschreibung ist bewusst weit gefasst: Sicherheitsrelevanter interner Zustand kann nach außen gelangen, wenn Vorgänge erkennbar unterschiedlich lange dauern. Das übliche Beispiel ist die Prüfung eines Passworts. Bei einem Agenten-Gateway gibt es eine weniger bekannte Variante desselben Problems: Der Aufrufer befindet sich bereits im Workflow des Entwicklers und kann viele strukturierte Tool-Aufrufe absetzen, ohne zu ermüden.

Die Lösung besteht nicht darin, jede Aktion exakt gleich lange dauern zu lassen. Dieses Versprechen ist meistens falsch, besonders sobald eine HTTP-Anfrage oder SSH-Verbindung den Rechner verlässt. Die Aufgabe ist enger und praktischer: Ermittle Zustände, die ein Agent nicht unterscheiden können sollte, behandle sie lokal vergleichbar und halte eine differenzielle Testsuite bereit, die neue schnelle Pfade entdeckt.

Den Tresorzustand als Daten behandeln, die der Aufrufer nicht ableiten darf

Ein gesperrter Tresor, fehlende Zugangsdaten, eine verweigerte Aktion und ungültige Zugangsdaten beim entfernten Dienst sind unterschiedliche Betriebszustände. Daraus folgt nicht automatisch, dass der Agent diese unterschiedlichen Fakten erfahren muss.

Schreibe zuerst auf, was jeder Aufrufer wissen darf. Das klingt selbstverständlich, wird aber oft übersprungen. Dann entscheidet die Reihenfolge der Implementierung über die Offenlegung. Eine frühe Tresorabfrage verrät, ob Zugangsdaten existieren. Eine frühe Autorisierungsprüfung verrät, ob ein Prozess eine genehmigte Sitzung besitzt. Ein sofortiger lokaler Fehler verrät, dass der Tresor gesperrt ist. Jeder Abkürzungspfad kann für sich vernünftig sein. Zusammengenommen geben sie dem Aufrufer eine Zustandsabfrage.

Trenne bei einem Aktions-Gateway diese Fragen:

  • Darf dieser Prozess überhaupt eine Aktion anfordern?
  • Ist der Tresor derzeit verfügbar, um sie auszuführen?
  • Existiert eine Zuordnung von Zugangsdaten für diese Aktion?
  • Hat der entfernte Dienst die Zugangsdaten akzeptiert?
  • Kann der Aufrufer eine dieser Antworten direkt sehen oder erhält er nur das Ergebnis der Aktion?

Wichtig ist die Unterscheidung zwischen Autorisierungszustand und Zustand der Zugangsdaten. Die Autorisierung beantwortet, ob ein bestimmter Agentenlauf eine Aktion aufrufen darf. Der Zustand der Zugangsdaten beantwortet, ob das Gateway über das Material verfügt, um diese Aktion auszuführen. Wenn du beides intern zusammenlegst, kann ein Timing-Ergebnis einem Agenten versehentlich verraten, dass Zugangsdaten existieren, nur weil das Gateway nach der Genehmigung danach gesucht hat.

Sallyports Tresorsperre ist bewusst absolut: Solange der Tresor gesperrt ist, wird jede Aktion verweigert. Das ist eine klare Sicherheitsgrenze, verdient aber trotzdem Timing-Tests, weil ein Aufrufer beobachten kann, wie die Ablehnung erzeugt wird. Das Ziel ist nicht vorzutäuschen, dass ein gesperrter Tresor eine entfernte Anfrage abschließen kann. Das Ziel ist, sicherzustellen, dass die lokale Ablehnungsbehandlung kein leicht wiederholbares Muster erzeugt, das mehr verrät als nötig.

OWASP macht denselben Punkt in seinem Authentication Cheat Sheet. Allgemeiner Fehlertext allein schließt ein Aufzählungsleck nicht, wenn ein Fehlerpfad mehr Arbeit erledigt als ein anderer. Die Empfehlung bezieht sich auf Authentifizierung, die technische Lehre gilt aber auch hier: Eine einheitliche Meldung, die unterschiedliche Ausführungspfade umschließt, kann über die verstrichene Zeit trotzdem Informationen preisgeben.

Vier Ergebnisse brauchen zwei verschiedene Timing-Verträge

Es ist beliebt, alle Ergebnisse zeitlich anzugleichen, weil sich das einfach formulieren lässt. Sobald ein Gateway Netzwerke aufrufen kann, ist es jedoch falsch. Du brauchst zwei Timing-Verträge, keine unmögliche universelle Dauer.

Der erste Vertrag gilt für lokale Ergebnisse. Das sind Zustände, bei denen das Gateway ablehnen sollte, bevor es eine Anfrage sendet: Der Tresor ist gesperrt, eine Sitzungsautorisierung fehlt oder wurde verweigert, für eine Aktion sind keine Zugangsdaten konfiguriert oder eine Genehmigung pro Aufruf wurde abgelehnt. Zustände, die dem Agenten gleich viele Informationen verraten sollen, sollten durch eine gemeinsame lokale Antwortstruktur laufen.

Der zweite Vertrag gilt für weitergeleitete Ergebnisse. Gültige und ungültige Zugangsdaten können denselben HTTP-Testserver oder SSH-Testhost erreichen. Ihre Gesamtdauer umfasst Transport, Verbindungswiederverwendung, Serverarbeit und Antwortübertragung. Das öffentliche Internet lässt sich nicht konstant schnell machen. Du kannst aber sicherstellen, dass das Gateway beide Ergebnisse über vergleichbare Pfade einfügt, sendet, protokolliert und abbildet. Für den entfernten Teil verwendest du einen kontrollierten Server, damit er während des Tests stabil bleibt.

Daraus ergibt sich diese Matrix:

ErgebnisVerlässt die Anfrage das Gateway?Womit sollte der Timing-Test es vergleichen?
Tresor gesperrtNeinMit anderen lokalen Ablehnungen, die nicht mehr Zustand verraten sollen
Autorisierung verweigertNeinMit anderen lokalen Ablehnungen, gemessen vor menschlicher Interaktion
Zugangsdaten fehlenNeinMit einem lokalen Fehler bei verfügbarem Tresor und derselben Antwortstruktur, sofern die Offenlegungsrichtlinie das verlangt
Zugangsdaten ungültigJaMit gültigen Zugangsdaten beim selben kontrollierten Upstream
Zugangsdaten gültigJaMit ungültigen Zugangsdaten und Varianten entfernter Fehler

Vergleiche fehlende Zugangsdaten nicht direkt mit einem erfolgreichen Aufruf einer Drittanbieter-API und erkläre den Test für fehlgeschlagen, weil einer schneller ist. Damit beweist du nur, dass eine Anfrage, die den Rechner nie verlassen hat, schneller ist als eine Internetanfrage. Das wusstest du bereits.

Teste stattdessen die Grenzen getrennt. Ein lokaler Aufrufer sollte Schwierigkeiten haben, die lokalen Zustände zu unterscheiden, die du verbergen willst. Ein weitergeleiteter Aufrufer sollte gültige und ungültige Testzugangsdaten nicht anhand des Gateway-Overheads unterscheiden können. Der Testserver kann nach derselben Verzögerung absichtlich unterschiedliche Statuscodes zurückgeben. So bleibt das semantische Ergebnis verschieden, während das Timing-Experiment aussagekräftig bleibt.

Es gibt eine harte Grenze: Wenn ein Aufrufer eine detaillierte Fehlermeldung mit dem Text „Zugangsdaten fehlen“ erhält, braucht er kein Timing mehr, um das zu wissen. Timing-Abwehr kann keine API retten, die den geheimen Zustand offen meldet. Lege zuerst die erlaubte Fehlersichtbarkeit fest.

Ein schneller Fehlschlag wird zum Orakel, wenn Aufrufer ihn wiederholen können

Ein Timing-Leck zeigt sich selten als dramatischer Unterschied. Häufiger fügt jemand beim Refactoring einen sinnvollen frühen Rücksprung ein.

Stell dir ein Gateway mit dieser Reihenfolge vor:

  1. Anfrage des Agenten analysieren.
  2. Benannte Zugangsdaten im Tresorindex suchen.
  3. Prüfen, ob der Agentenprozess eine Sitzungsfreigabe besitzt.
  4. Anfrage erstellen und weiterleiten.

Fehlende Zugangsdaten führen bei Schritt zwei zum Abbruch. Vorhandene Zugangsdaten bei einem nicht genehmigten Prozess führen weiter zu Schritt drei. Die externe Antwort könnte in beiden Fällen gleich lauten: „Aktion nicht verfügbar.“ Der zweite Pfad enthält jedoch einen Treffer im Tresorindex, eine Autorisierungsabfrage, die Vorbereitung des Audit-Eintrags und möglicherweise die Erstellung einer Genehmigungskarte. Ein Aufrufer kann jede Eingabe oft ausführen und die Ergebnisse sortieren.

Das Leck wird schlimmer, wenn der Agent Aliase für Zugangsdaten auswählen kann. Er kann eine Wortliste mit Namen wie staging, production oder deploy durchprobieren, ebenso Namen, die er in einem Repository gefunden hat. Eine erfolgreiche Aktion ist nicht nötig. Ein schneller und ein langsamer Bereich reichen aus.

Die defensive Reihenfolge hängt von deiner Offenlegungsrichtlinie ab, eine sicherere Form sieht aber so aus:

  1. Anfrage ohne von Geheimnissen abhängige Verzweigungen validieren.
  2. Die absolute Tresorsperre anwenden.
  3. Prozess- oder Sitzungsautorisierung anwenden.
  4. Aktion und Zugangsdaten erst auflösen, nachdem der Aufrufer die Kontrollen passiert hat, die diesem Wissen vorausgehen sollen.
  5. Lokale Fehler, die nicht unterscheidbar bleiben müssen, durch dieselbe Protokollierung, Fehlerformung und Antwortvervollständigung führen.

Lies das nicht als Aufforderung, für einen nicht autorisierten Prozess geheime Arbeit auszuführen. Du solltest keine Zugangsdaten entschlüsseln und keinen echten Autorisierungs-Header erstellen, nur um Zeit zu verbrauchen. Es geht darum, zustandsabhängige Abkürzungen an der beobachtbaren Grenze zu vermeiden, nicht Ablehnungen in geheimen Zugriff zu verwandeln.

Ein typischer schlechter Patch ist sleep(100ms) vor jeder Ablehnung. Er macht ein Demo-Diagramm schöner und erzeugt dann drei Probleme. Die Dauer unterscheidet sich nach wie vor durch Scheduler-Rauschen und Cache-Verhalten. Die Verzögerung belastet legitime Benutzer. Vor allem kann ein wiederholender Aufrufer zufällige oder feste Auffüllung herausmitteln, wenn sich die zugrunde liegenden Pfade weiterhin unterscheiden. Es ist zuverlässiger, die erforderliche Arbeit anzugleichen, als ungleiche Arbeit mit Verzögerungen zu überdecken.

An der Grenze messen, die der Agent tatsächlich sieht

Instrumentierung innerhalb des Gateways hilft bei der Diagnose, ist aber nicht die primäre Sicherheitsmessung. Der Agent beobachtet die Zeit zwischen dem Senden eines Tool-Aufrufs und dem Empfang seines endgültigen Ergebnisses. Dort beginnt und endet dein End-to-End-Differenztest.

Verwende einen dedizierten Testtreiber, der sich wie ein Agenten-Client verhält. Er sollte mit einer bekannten Prozessidentität starten, eine Anfrage senden, auf eine vollständige Antwort warten, eine monotone Dauer aufzeichnen und die Ergebnisbezeichnung außerhalb der gemessenen Anfrage speichern. Gib während des Laufs keine Bezeichnungen aus. Konsolenausgabe, Tracing-Exporter und Debug-Logging können kurze lokale Pfade so stark verzerren, dass die gesuchte Regression verborgen bleibt.

Ein praktischer Datensatz braucht mehr als eine Zahl:

{
  "case": "vault_locked",
  "sequence": 184,
  "elapsed_us": 12746,
  "result_class": "local_denial",
  "connection_mode": "fresh",
  "run_id": "test-run-7"
}

Verwende eine monotone Uhr. Wanduhrzeiten springen bei der Zeitsynchronisierung und eignen sich schlecht für Vergleiche im Subsekundenbereich. Zeichne Mikrosekunden oder Nanosekunden auf, wenn die Plattform das anbietet, und gib die Ergebnisse bei der Auswertung in Millisekunden aus. Präzision in der Datei bedeutet nicht Genauigkeit der Messung, verhindert aber, dass du vor der Analyse Informationen wegwirfst.

Führe vor der Datensammlung eine Aufwärmphase aus. Die ersten Aufrufe starten möglicherweise die Anwendung, laden Code, initialisieren ein Tresor-Handle, erstellen einen Verbindungspool oder befüllen einen Cache. Diese Effekte sind betrieblich real, überdecken aber oft den Unterschied im stabilen Betrieb, den du untersuchen willst. Teste den Kaltstart separat, wenn er für Agenten sichtbar ist. Verwerfe das unbequeme Verhalten des ersten Aufrufs nicht stillschweigend, nur weil es Diagramme unschöner macht.

Vertausche die Reihenfolge der Fälle zufällig. Wenn du zuerst 500 Aufrufe mit gesperrtem Tresor und danach 500 Aufrufe mit fehlenden Zugangsdaten ausführst, werden Temperaturzustand, Garbage Collection, Hintergrundaktivität und Verbindungswiederverwendung zu Störfaktoren. Verschachtle die Fälle mithilfe eines Startwerts für den Zufallsalgorithmus, damit Fehler reproduzierbar bleiben.

Wechsle außerdem zwischen neuen und wiederverwendeten Verbindungen, wenn das Client-Protokoll beides unterstützt. Ein Leck, das bei einer warmen Verbindung verschwindet, kann trotzdem relevant sein, wenn ein Agent kurzlebige Clients erstellt. Ein Leck, das nur bei Wiederverwendung auftritt, kann einen Cache verraten, der nach dem Vorhandensein von Zugangsdaten arbeitet.

Einen kontrollierten Upstream bauen, der Bedeutung und Verzögerung trennt

Sallyport installieren
Ein neuer Agentenprozess erhält eine Genehmigungskarte mit seiner Codesignatur, bevor der Lauf erlaubt wird.

Ungültige Zugangsdaten sind der Testfall, bei dem Teams am häufigsten Fehler machen. Sie richten den Test auf einen echten Dienst, senden absichtlich ein falsches Token und vergleichen es mit einem erfolgreichen Aufruf. Ratenbegrenzungen des Anbieters, regionale Weiterleitung, TLS-Sitzungswiederaufnahme und Schutzmechanismen gegen Missbrauch werden dann Teil des Ergebnisses. Das ist kein Timing-Test des Gateways.

Baue einen kleinen Testserver unter deiner Kontrolle. Er sollte eine bekannte Testzugabe aus derselben Header-Struktur lesen, die dein Gateway einfügt, eine festgelegte Menge Arbeit ausführen und bei gültigen und ungültigen Zugangsdaten einen unterschiedlichen Antworttext und Status zurückgeben. Der ungültige Pfad darf nicht vorzeitig abbrechen.

Dieser Vertrag für eine Testvorrichtung reicht zum Beispiel aus:

Request header: Authorization: Bearer test-good
Response: 200 {"fixture":"accepted"}

Request header: Authorization: Bearer test-bad
Response: 401 {"fixture":"rejected"}

Both requests: wait until the same server-side target duration has elapsed

Die Zieldauer sollte länger sein als gewöhnliches lokales Scheduler-Rauschen, aber kurz genug, damit die Suite schnell bleibt. Leite sie aus Messungen auf deinem eigenen Testrechner ab, statt eine Zahl aus einem Blogbeitrag zu übernehmen. Entscheidend ist, dass beide Zweige der Testvorrichtung denselben Analyse-, Uhr-, Warte- und Antwortpfad durchlaufen, bevor sie sich semantisch unterscheiden.

Für HTTP führst du die Testvorrichtung über Loopback aus, wenn du Gateway und Client isolieren willst. In einem zweiten Job verwendest du einen kontrollierten entfernten Host, wenn du sehen möchtest, wie sich normales Netzwerkrauschen auf die Erkennung auswirkt. Halte die Berichte getrennt. Ein Loopback-Ergebnis zeigt, ob sich die lokale Implementierung verändert hat. Ein entferntes Ergebnis zeigt, ob dein Test unter realistischen Transportabweichungen empfindlich bleibt.

Für SSH verwendest du ein Testkonto und einen Befehl, der nach der Authentifizierung auf bekannte Weise beendet wird. Teste nicht, indem du wiederholt eine produktive Bastion fehlschlagen lässt. SSH-Server können Fehler absichtlich verlangsamen, Konten sperren oder zusätzliche Protokollierungsarbeit ausführen. Das sind sinnvolle Schutzmaßnahmen, machen deine Messung aber zu einer Aussage über den Server statt über das Gateway.

Sallyport sendet HTTP-Anfragen mit Bearer-, Basic- oder benutzerdefinierten Header-Zugangsdaten und verwendet den zustandslosen, gebündelten Helfer sp-ssh für SSH. Das sind getrennte Kanäle und sie brauchen getrennte Testvorrichtungen. Ein gleichmäßiges Ergebnis bei einem HTTP-Header-Pfad sagt nichts über einen SSH-Pfad aus, der Schlüssel auflöst, einen Helfer startet und eine Verbindung aushandelt.

Verteilungen vergleichen und anschließend versuchen, den Zustand zu klassifizieren

Durchschnittswerte verbergen Timing-Lecks. Wenn 90 Prozent der Aufrufe 12 Millisekunden dauern und 10 Prozent wegen eines Cache-Fehlers bei vorhandenen Zugangsdaten 80 Millisekunden benötigen, kann der Durchschnitt einem anderen Fall ausreichend ähnlich sehen. Ein Agent kann jedoch den schnellen Cluster ausnutzen.

Bewahre für jeden Fall die vollständige Menge der Messwerte auf. Gib mindestens Median, untere und obere Perzentile, Minimum, Maximum und Anzahl der Stichproben aus. Ein Histogramm zeigt oft mehr als eine Tabelle, weil es zwei Cluster sofort sichtbar macht.

Mache den Test anschließend adversarial. Gib einem bewusst einfachen Klassifikator nur die verstrichene Dauer und bitte ihn, das verborgene Label zu erraten. Beginne mit einem Schwellenwert-Klassifikator. Er wählt eine Grenze, zum Beispiel „unter X Mikrosekunden bedeutet fehlende Zugangsdaten“, und meldet die Genauigkeit auf Daten, die nicht zur Auswahl dieser Grenze verwendet wurden. Wenn ein Klassifikator mit einer einzigen Schwelle die naive Baseline wiederholt deutlich übertrifft, gibt es ein beobachtbares Signal, das untersucht werden sollte.

Ein kleines Python-Analyseskript kann diese Regression sichtbar machen, ohne so zu tun, als würde es kryptografische konstante Laufzeit beweisen:

from statistics import median

samples = {
    "vault_locked": [...],
    "credential_missing": [...],
}

for name, values in samples.items():
    ordered = sorted(values)
    p10 = ordered[int(len(ordered) * 0.10)]
    p90 = ordered[int(len(ordered) * 0.90)]
    print(name, {"n": len(values), "p10": p10,
                 "median": median(values), "p90": p90})

best = None
all_values = sorted(set(samples["vault_locked"] + samples["credential_missing"]))
for cutoff in all_values:
    correct = 0
    total = 0
    for label, values in samples.items():
        for value in values:
            guess = "vault_locked" if value <= cutoff else "credential_missing"
            correct += (guess == label)
            total += 1
    score = correct / total
    if best is None or score > best[0]:
        best = (score, cutoff)

print({"best_training_accuracy": best[0], "cutoff_us": best[1]})

Teile die Stichproben in Trainings- und Holdout-Gruppen auf, bevor du die Grenze auswählst. Andernfalls passt sich das Skript an das Rauschen an und gratuliert sich selbst. Wenn du einen umfangreicheren Klassifikator hinzufügst, behandle ihn als zusätzliche Diagnose. Ein kompliziertes Modell kann winzige Muster finden, die kein praktischer Agent nutzen kann. Eine einfache Schwelle zeigt dagegen die peinlichen Lecks, die Ingenieure tatsächlich einbauen.

Lege keine universelle Bedingung wie „Alle Mediane müssen innerhalb von fünf Millisekunden liegen“ fest. Diese Schwelle hat auf unterschiedlichen Rechnern und in unterschiedlichen Testaufbauten keine feste Bedeutung. Verwende eine Baseline aus deiner kontrollierten Umgebung, prüfe, ob die Verteilungen wie erwartet überlappen, und lasse den Test fehlschlagen, wenn eine Änderung Zustände, die du verbergen wolltest, stabil trennbar macht.

Ein Vergleich mit konstanter Laufzeit löst ein kleineres Problem

Aktionen hinter einem Gateway bündeln
Agenten rufen das gebündelte sp-mcp-Shim auf, statt selbst Zugangsdaten zu erhalten.

Wenn Entwickler „Timing-Angriff“ hören, greifen sie oft zu einer Vergleichsfunktion mit konstanter Laufzeit. Diese Funktion ist bei gleich langen Geheimnissen, Signaturen, MACs und Tokens wichtig. Sie macht aber nicht den gesamten Anfragepfad eines Aktions-Gateways konstant schnell.

Das Go-Paket crypto/subtle stellt ConstantTimeCompare für Byte-Slices mit gleichem Inhalt bereit. Das ist die richtige Art Primitive, wenn der Vergleich selbst ein datenunabhängiges Verhalten braucht. Die Funktion kann aber keinen Pfad angleichen, der vor dem Öffnen des Tresors zurückkehrt, eine Genehmigungskarte rendert oder eine Netzwerkverbindung herstellt.

Verwende Vergleiche mit konstanter Laufzeit dort, wo du sensible Werte mit festem Format vergleichst. Prüfe danach den umgebenden Kontrollfluss. Ein sauberer Vergleich innerhalb eines Pfads, der bei fehlenden Zugangsdaten sofort zurückkehrt, verrät weiterhin, ob Zugangsdaten vorhanden sind.

Darum solltest du das gesamte Problem auch nicht „konstante Laufzeit“ nennen. Für ein Desktop-Gateway, das HTTP- und SSH-Aufrufe ausführen kann, ist konstante Laufzeit von Ende zu Ende weder erreichbar noch nötig. Die Anforderung ist enger: Sensible lokale Zustände dürfen für den Aufrufer kein bequem klassifizierbares Timing-Signal erzeugen.

Zu den Beispielen von MITRE für CWE-208 gehören frühe Rücksprünge bei Passwortprüfungen und unterschiedliche Arbeit für vorhandene und nicht vorhandene Konten. Das Muster bleibt gleich, auch wenn überhaupt kein Passwortvergleich stattfindet. Der beobachtbare Unterschied entsteht durch den Entscheidungspfad, nicht durch einen einzelnen unsicheren Gleichheitsoperator.

Genehmigungsabläufe brauchen eine explizite Messgrenze

Eine Genehmigung pro Sitzung oder pro Aufruf bringt einen Menschen in die Zeitmessung. Die Dauer des gesamten Aufrufs umfasst dann die Zeit, die jemand braucht, um eine Karte zu bemerken, die Prozessidentität zu lesen, zu entscheiden, sich mit Touch ID zu authentifizieren und zu klicken. Diese Dauer schwankt aus Gründen, die nichts mit dem Tresorzustand zu tun haben.

Versuche nicht, das mit Auffüllung zu verbergen. Du verschlechterst die Interaktion und erreichst trotzdem nicht, dass Menschen nach einer Uhr handeln.

Teile den Ablauf stattdessen in automatische und menschlich gesteuerte Abschnitte. Der automatische Abschnitt beginnt, wenn der Agent den Aufruf sendet, und endet, sobald das Gateway entweder eine Ablehnung ohne Aufforderung erzeugt oder eine Genehmigungsanfrage präsentiert hat. Miss diesen Abschnitt über verschiedene Zustände hinweg. Der menschlich gesteuerte Abschnitt beginnt bei der Anzeige und endet mit Genehmigung, Ablehnung, Timeout oder Prozessende. Protokolliere ihn für den Betrieb, verwende ihn aber nicht als Ziel für Timing-Gleichheit im Seitenkanal.

Du brauchst außerdem einen Testmodus, der vor der Messung einen stabilen Genehmigungszustand für weitergeleitete Aufrufe herstellt. Andernfalls enthält deine Verteilung für „gültige Zugangsdaten“ einmal eine Genehmigungskarte und überspringt sie für den Rest der Sitzung. Das erzeugt einen großen Ausreißer bei der ersten Stichprobe und verdeckt subtilere Unterschiede.

Eine gute Suite enthält eigene Fälle für diese Fragen:

  • Verweigert ein gesperrter Tresor die Anfrage, bevor eine von der Genehmigung abhängige Auflösung der Zugangsdaten stattfindet?
  • Erhält ein nicht genehmigter Prozess unabhängig davon, ob für eine Aktion Zugangsdaten konfiguriert sind, dieselbe Behandlung vor der Aufforderung?
  • Durchlaufen gültige und ungültige Testzugangsdaten nach der Genehmigung einer Sitzung vergleichbare Gateway-Pfade zum kontrollierten Upstream?
  • Führt eine Ablehnung der Genehmigung pro Aufruf vor der Aufforderung zu zusätzlicher, von Zugangsdaten abhängiger Arbeit?

Sallyport identifiziert einen neuen Agentenprozess in der Genehmigungskarte anhand seiner Codesignatur. Die Autorisierung pro Sitzung gilt nur, solange dieser Prozess läuft. Die Timing-Suite sollte beim Test des Verhaltens beim ersten Aufruf absichtlich neue Agentenprozesse erzeugen und beim Test einer genehmigten Sitzung einen bekannten Prozess am Leben halten. Wenn du diese Modi mischst, ist das Ergebnis nicht interpretierbar.

Audit-Arbeit darf keinen von Geheimnissen abhängigen schnellen Pfad erzeugen

Eine weitere Policy-Engine vermeiden
Sallyport verwendet eine feste Entscheidungskette mit drei Kontrollen, keine Richtliniensprache und keine Regel-Engine.

Audit-Logging verursacht oft das letzte Timing-Leck, weil es als Buchhaltung statt als Teil der Sicherheitsgrenze behandelt wird. Eine abgelehnte Anfrage zeichnet möglicherweise nur ein knappes Ereignis auf. Eine erfolgreiche Anfrage kann dagegen einen detaillierten Eintrag anlegen, ihn in eine Kette hashen und in verschlüsselten Speicher schreiben. Dieser Unterschied kann vertretbar sein, wenn der Erfolg ohnehin sichtbar ist. Gefährlich wird er, wenn zwei lokale Ablehnungen unterschiedlich viel über einen verborgenen Zustand verraten.

Lege fest, welche Felder jedes Ergebnis sicher protokollieren darf, und sorge dafür, dass gleichartige lokale Fehler gleichartige Audit-Arbeit ausführen. Du musst keine erfundenen Zugangsdaten schreiben und keine Dummy-Anfragen senden. Du musst aber einen Pfad vermeiden, in dem „Zugangsdaten fehlen“ das Journal umgeht, während „Tresor gesperrt“ eine aufwendigere Suche und einen Eintrag ausführt, wenn der Agent diese Zustände nicht unterscheiden können soll.

Halte die Audit-Prüfung aus dem Anfragepfad heraus. Die Prüfung ist eine Betreiberaktion mit einem anderen Zweck und einem anderen Timing-Profil. Sallyports Audit-Kette kann mit sp audit verify offline über dem Chiffretext geprüft werden, ohne Tresorschlüssel. Das verhindert, dass die Prüfung einen geheimen Lesevorgang benötigt, nimmt dir aber nicht die Pflicht ab, das Verhalten beim Protokollieren während einer Anfrage zu testen.

Füge interne Spans für Anfrageanalyse, Autorisierung, Tresorsperre, Auflösung der Zugangsdaten, Audit-Anhang, Weiterleitung und Antwortzuordnung hinzu. Gib diese Spans nicht an den Agenten weiter. Wenn der End-to-End-Test eine Trennung findet, vergleiche die aggregierten Span-Dauern der Fälle, um die neu abweichende Stufe zu finden.

Der Fehler besteht darin, detailliertes Tracing erst nach dem Auftreten einer Timing-Regression hinzuzufügen. Ergänze die Instrumentierung früh, schütze sie mit einer Test- oder lokalen Diagnoseeinstellung und stelle sicher, dass der normale Anfragepfad keine synchronen Logs ausgibt, deren Kosten von Zugangsdaten oder Ergebnis abhängen.

Differenztests neben den Code setzen, der Verzweigungen hinzufügt

Eine Timing-Suite gehört in dieselbe Änderungsprüfung wie Autorisierungstests. Die wahrscheinlichsten Regressionen entstehen durch normale Wartungsänderungen: einen Shortcut für fehlende Konfiguration, einen Cache für Aliase von Zugangsdaten, ein neues Audit-Feld, eine bessere Fehlermeldung oder ein Refactoring, das die Tresorsuche vor die Prozessgenehmigung verschiebt.

Führe eine kompakte lokale Suite bei jeder Änderung aus, die Tresorzugriff, Autorisierung, Aktionsauflösung, Fehlerzuordnung, Transportaufbau oder Audit-Schreibvorgänge betrifft. Sie kann Loopback-Testvorrichtungen und eine moderate Anzahl von Stichproben verwenden. Vor Releases führst du in einer ruhigen, kontrollierten Umgebung eine längere Suite mit zufälliger Reihenfolge aus und zeichnest Kalt- und Warmmodi getrennt auf.

Die Testausgabe sollte etwas enthalten, mit dem ein Reviewer arbeiten kann. Das ist besser als ein grünes Häkchen mit einer unerklärten Gesamtzahl:

case pair: vault_locked vs credential_missing
samples: 400 per case, randomized order
median: 12.7 ms vs 13.1 ms
p10-p90: 11.8-14.0 ms vs 11.9-14.3 ms
holdout threshold accuracy: 51.4%
result: within baseline

Und das ist besser, als das Problem unter einem allgemeinen Performancefehler zu verbergen:

case pair: vault_locked vs credential_missing
samples: 400 per case, randomized order
median: 4.2 ms vs 19.6 ms
p10-p90: 3.9-4.7 ms vs 18.2-22.1 ms
holdout threshold accuracy: 99.1%
result: investigate credential lookup before authorization

Der zweite Bericht behauptet nicht, dass ein Angreifer auf einem ausgelasteten Arbeitsplatzrechner immer eine Genauigkeit von 99,1 Prozent erreicht. Er zeigt dem Entwickler, dass der lokale Code eine klare, wiederholbare Trennung erzeugt hat. Das reicht aus, um die Änderung zu stoppen, bis die Reihenfolge der Verzweigungen oder die Antwortstruktur korrigiert ist.

Verdecke das Ergebnis nicht mit einer beliebigen Verzögerung. Verschiebe von Geheimnissen abhängige Arbeit hinter die richtige Sperre, entferne unnötige zustandsabhängige Arbeit und teste erneut von der Grenze des Agenten aus. Das nützliche Ergebnis ist kein perfekt flaches Diagramm. Es ist ein Agent, der eine Stoppuhr nicht in ein Inventar dessen verwandeln kann, was sich hinter dem Tresor befindet.

FAQ

Sollten ein gesperrter Tresor und fehlende Zugangsdaten dieselbe Fehlermeldung zurückgeben?

Teste beide Fälle als getrennte Zustände, auch wenn die sichtbare Antwort absichtlich ähnlich ist. Ein gesperrter Tresor bedeutet, dass das Gateway keine Aktion ausführen kann. Eine fehlende Zugangsdaten-Zuordnung bedeutet, dass die ausgewählte Aktion nicht konfiguriert ist. Bei ungültigen Zugangsdaten hat die Anfrage den entfernten Dienst erreicht und ist dort fehlgeschlagen. Wenn sich die lokalen Laufzeiten dieser Pfade stark unterscheiden, kann ein Agent sie trotzdem klassifizieren.

Wie teste ich das Timing, wenn ein Genehmigungsdialog einen Klick durch einen Menschen erfordert?

Die Dauer einer menschlichen Genehmigung ist kein sinnvolles Timing-Ziel, weil sie von einer Person abhängt. Miss den automatischen Abschnitt, bevor die Genehmigungskarte erscheint, und teste den genehmigten Pfad anschließend separat mit einer stabilen Testautorisierung. Behandle die Reaktionszeit des Benutzers nicht als Sicherheitsauffüllung.

Reicht es für Timing-Tests aus, die durchschnittliche Antwortzeit zu vergleichen?

Nein. Ein Test, der nur die durchschnittliche Dauer vergleicht, übersieht mehrere Verteilungen, Cache-Effekte und einen kurzen Pfad mit schnellem Abbruch, der in einem verrauschten Durchschnitt untergeht. Zeichne einzelne Messwerte auf, vergleiche Perzentile und versuche einen einfachen Klassifikator, der den Zustand anhand der verstrichenen Zeit errät.

Wie teste ich ungültige Zugangsdaten, ohne eine echte API aufzurufen?

Verwende einen kontrollierten Testserver, der bekannte gültige und ungültige Testzugangsdaten erkennt, für beide dieselbe feste Zeit wartet und anschließend unterschiedliche Statuscodes zurückgibt. So kannst du das Verhalten des Gateways messen, ohne es mit einer unberechenbaren API eines Drittanbieters zu verwechseln. Führe niemals umfangreiche Timing-Tests gegen einen produktiven Anbieter aus.

Behebt eine zufällige Verzögerung einen Timing-Seitenkanal?

Nein. Zufälliges Rauschen macht ein klares Signal bei wenigen Messungen schwerer erkennbar, doch wiederholte Messungen können den Effekt herausmitteln. Außerdem wird die normale Nutzung langsamer und das Timing-Verhalten zu einem Wahrscheinlichkeitsproblem. Sorge stattdessen dafür, dass vergleichbare Pfade dieselbe notwendige Arbeit ausführen.

Welche Zeitdifferenz zwischen Autorisierungsergebnissen ist sicher?

Das sinnvolle Ziel ist, dass ein Klassifikator Zustände, die an der Messstelle privat bleiben sollen, nicht zuverlässig trennen kann. Es gibt kein allgemeingültiges Zeitbudget in Millisekunden, weil ein lokaler Prozess, ein Loopback-Server und eine Internet-API unterschiedlich starkem Rauschen ausgesetzt sind. Halte eine Baseline fest und lasse den Test fehlschlagen, wenn eine Codeänderung eine stabile Trennung erzeugt, die zuvor nicht vorhanden war.

Sollten Netzwerkfehler mit fehlenden Zugangsdaten gruppiert werden?

Behandle Transportfehler als eigene Kategorie. DNS-Probleme, eine abgelehnte Verbindung und ein Timeout beim entfernten Dienst können Netzwerkbedingungen verraten, sagen aber nicht unbedingt aus, ob ein Eintrag im Tresor vorhanden ist. Fasse zusammenhanglose Fehler nicht so stark zusammen, dass Betreiber defekte Verbindungen nicht mehr diagnostizieren können.

Von wo aus sollte ich das Timing in einem Agenten-Gateway messen?

Teste an der vom Agenten beobachtbaren Grenze: vom Schreiben der MCP-Anfrage bis zum Empfang des endgültigen Ergebnisses oder Fehlers. Ergänze außerdem engere Tests für Tresorzugriff, Autorisierung und Weiterleitung, damit ein fehlgeschlagener Differenztest zeigt, welche Stufe sich verändert hat. Eine End-to-End-Stoppuhr findet Lecks, interne Spans erklären sie.

Können Timing-Tests Geheimnisse schützen, wenn der Agent weitreichenden Zugriff hat?

Tests können versehentliche Offenlegung verringern, aber sie können keine Tatsache verbergen, die der Agent ausdrücklich erfahren darf. Wenn ein Agent Zugangsdaten-Namen auflisten, Statusanzeigen einsehen oder unterschiedliche detaillierte Fehler erhalten kann, ist Timing nicht mehr das Hauptproblem. Entferne zuerst unnötige Zustandsinformationen und erschwere anschließend die Klassifizierung der verbleibenden Pfade anhand ihrer Dauer.

Wie oft sollte ich Timing-Seitenkanaltests ausführen?

Behandle die Timing-Suite als Regressionstest, nicht als einmalige Prüfung. Führe eine kurze Version bei jeder Änderung an Autorisierung, Tresorzugriff, Fehlerzuordnung oder Anfrageweiterleitung aus. Vor Releases solltest du in einer kontrollierten Umgebung eine größere, zufällig angeordnete Stichprobe ausführen. Timing-Lecks kehren oft zurück, wenn jemand einen harmlos wirkenden frühen Rücksprung einbaut.

Sallyport

Sallyport führt API-Aufrufe und SSH-Befehle für Ihren KI-Agenten aus. Die Schlüssel bleiben in einem lokalen Tresor auf Ihrem Mac; Sie geben jeden Lauf frei, und jede Aktion landet in einem versiegelten Journal.

© 2026 Sallyport · Open Source unter Apache-2.0 · Oleg Sotnikov