# 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:

| Ergebnis | Verlässt die Anfrage das Gateway? | Womit sollte der Timing-Test es vergleichen? |
| --- | --- | --- |
| Tresor gesperrt | Nein | Mit anderen lokalen Ablehnungen, die nicht mehr Zustand verraten sollen |
| Autorisierung verweigert | Nein | Mit anderen lokalen Ablehnungen, gemessen vor menschlicher Interaktion |
| Zugangsdaten fehlen | Nein | Mit einem lokalen Fehler bei verfügbarem Tresor und derselben Antwortstruktur, sofern die Offenlegungsrichtlinie das verlangt |
| Zugangsdaten ungültig | Ja | Mit gültigen Zugangsdaten beim selben kontrollierten Upstream |
| Zugangsdaten gültig | Ja | Mit 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:

```json
{
  "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

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:

```text
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:

```python
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

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

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:

```text
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:

```text
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.
