Zusammenfassung

  • Ein Index der dynamischen HPACK-Tabelle lässt sich nur im richtigen Kontext, in der richtigen Richtung und an der richtigen Stelle einer HTTP/2-Verbindung auslegen.
  • Eine rekonstruierte Feldliste ist kein Cache-Treffer: Schlüssel, gespeicherte Antwort, Frische, Validierung, Berechtigung und Anwendungsergebnis brauchen eigene Belege.

Im Paketmitschnitt steht nur noch eine Zahl. Trotzdem erscheint im Betriebsbericht ein vollständiger Satz: „Die Antwort kam aus dem HPACK-Cache.“ Der erste Teil kann stimmen, der zweite ist eine Erfindung der Metrik.

HPACK kann ein zuvor gesehenes Feld durch einen kurzen Tabellenverweis darstellen. Ein HTTP-Cache kann eine frühere Antwort verwenden, um eine neue Anfrage zu bedienen. Beide sparen Übertragung, doch nur einer speichert Antworten und entscheidet über deren Wiederverwendung.

Roberto Peon hat diese Kompressionsschicht mitdefiniert. Zusammen mit Hervé Ruellan verfasste er RFC 7541. Sein Datatracker-Profil führt außerdem RFC 7540, die ursprüngliche HTTP/2-Spezifikation. Eine QCon-Biografie von 2012 bezeichnet ihn damals als Google-Ingenieur und Mitentwickler von SPDY. Das ist historische Einordnung, keine aktuelle Beschäftigungsaussage. RFC 9113 hat andere Autoren.

Opaque Oktette, feste Reihenfolge

RFC 7541 beschreibt eine Feldliste als geordnete Sammlung von Name-Wert-Paaren. Gleiche Paare dürfen mehrfach vorkommen. Auf der Kompressionsebene sind Namen und Werte opaque Oktettfolgen. Nach dem Dekomprimieren muss ihre Reihenfolge erhalten sein.

Damit endet zunächst die Aufgabe. Wenn der Decoder cache-control: private ausgibt, hat er die Bedeutung von private noch nicht angewendet. Er hat auch keine Antwort gespeichert. Erst die HTTP-Schicht interpretiert das Feld in seinem Nachrichtenzusammenhang.

HPACK besitzt eine statische und eine dynamische Tabelle. Die statische Tabelle ist vordefiniert und unveränderlich. Die dynamische beginnt leer, wird während der Verarbeitung ergänzt, darf Duplikate enthalten und entfernt alte Einträge, um eine Größenobergrenze einzuhalten.

Ein Index verweist in diesen veränderlichen Adressraum. Neue Einträge verschieben Positionen, entfernte Einträge verschwinden. Auf einer anderen Verbindung kann dieselbe Zahl etwas anderes bedeuten oder ungültig sein. Der Index ist ein Kompressionsbefehl, keine dauerhafte Identität des Feldes.

Jede Richtung führt ein eigenes Gedächtnis

Bei bidirektionaler Nutzung trennt RFC 7541 die dynamischen Kodier- und Dekodiertabellen eines Endpunkts vollständig. Der Client kodiert Anfragen mit einem anderen Kontext, als er Antworten dekodiert. Beim Server gilt das spiegelbildlich.

RFC 9113 ordnet jedem Endpunkt pro Verbindung einen HPACK-Kodier- und einen Dekodierkontext zu. Alle Feldblöcke nutzen diese Kontexte; die dynamische Tabelle ist ihr wesentlicher veränderlicher Zustand.

Für einen Nachweis genügt deshalb nicht „Eintrag auf der Verbindung vorhanden“. Es fehlen Endpunkt, Richtung, Blockreihenfolge und vorheriger Tabellenstand. Wird eine Einfügung ausgelassen, kann ein späterer Index beim Sender und Empfänger verschiedene Oktette bezeichnen.

Auch ein Feldblock, dessen Nachricht verworfen werden soll, muss vollständig zusammengesetzt und dekomprimiert werden. Seine Befehle können den Zustand verändern, auf den spätere Streams angewiesen sind. Das Wegwerfen der Anwendungsausgabe erlaubt nicht, die Kompressionsgeschichte zu überspringen.

Eine ausgehandelte Grenze ist kein Aufbewahrungsversprechen

Zu Beginn einer HTTP/2-Verbindung beträgt die anfängliche Obergrenze 4.096 Byte. Der Decoder teilt seine Grenze mit SETTINGS_HEADER_TABLE_SIZE mit. Der Encoder kann eine geringere wirksame Größe verwenden und meldet Änderungen über eine HPACK-Anweisung.

Die 4.096 Byte sind ein Protokollstartwert, keine Erhebung realer Konfigurationen. Eine Verringerung wird zudem erst im geordneten Zusammenspiel mit der Bestätigung der Einstellungen wirksam. Danach muss der nächste Feldblock gegebenenfalls mit einer passenden Größenänderung beginnen.

Einfügungen verdrängen ältere Einträge. Ist ein einzelner Eintrag größer als das Maximum, wird die Tabelle geleert und der Eintrag nicht behalten. Dieses Verhalten beschreibt ein begrenztes Wörterbuch, kein Archiv.

Eine Abweichung betrifft die Verbindung. Kann ein Feldblock nicht dekomprimiert werden, verlangt RFC 9113 einen Verbindungsfehler vom Typ COMPRESSION_ERROR. Der auslösende Block gehört zwar zu einem Stream, doch der beschädigte Kontext wirkt auf weitere Streams derselben Richtung. Das ist kein Urteil über die fachliche Richtigkeit der Anfrage.

Ein HTTP-Cache hat ein anderes Mandat

RFC 9111 definiert einen Cache als lokalen Speicher für Antwortnachrichten samt Teilsystem für Speicherung, Abruf und Löschung. Der Zweck ist, mit einer früheren Antwort eine spätere Anfrage zu erfüllen, sofern die Regeln es erlauben.

Die Auswahl braucht einen Cache-Schlüssel aus mindestens Methode und Ziel-URI. Vary kann weitere Anfragenfelder einbeziehen. Der Cache bewertet Frische, zulässige veraltete Nutzung oder erfolgreiche Validierung und beachtet Direktiven wie no-store, private und no-cache.

Ein HPACK-Eintrag besitzt weder eine Ziel-URI noch einen gespeicherten Antwortkörper oder eine Frischeuhr. Er kann die Oktette von etag: "blau" behalten, ohne die zugehörige Darstellung zu kennen. Er kann age: 300 komprimieren, ohne das aktuelle Alter auszurechnen.

Das Feld cache-control: private zeigt den Unterschied. Der Encoder kann es wegen häufiger Wiederholung indexieren. Dadurch entsteht kein privater Cache, und über einen gemeinsamen Cache ist nichts entschieden. Die Cache-Implementierung legt die Semantik später aus. Der HPACK-Eintrag kann längst entfernt sein, während die Antwort gespeichert bleibt; umgekehrt kann er bestehen, obwohl keine Antwort gespeichert wurde.

Auch ein echter Cache-Treffer muss keine dynamische Referenz erzeugen. Die Felder können literal, statisch referenziert oder auf einer neuen Verbindung mit leerer Tabelle übertragen werden. Kompressionstreffer und Antwortwiederverwendung sind weder notwendig noch hinreichend füreinander.

Dekomprimierbar heißt nicht vertrauenswürdig

Ein gültiger Index belegt einen engen Sachverhalt: Der Empfänger besaß an dieser Stelle kompatiblen Zustand und konnte das Feld rekonstruieren. Danach folgen andere Prüfungen. HTTP/2 validiert Feldnamen und Werte; TLS liefert eigene Kanaleigenschaften; Vermittler können Nachrichten verändern; die Anwendung kann ein korrekt rekonstruiertes Feld ablehnen.

Auch Vertraulichkeit folgt nicht automatisch. RFC 7541 warnt davor, dass ein Angreifer Tabellenzustand sondieren kann, wenn er Felder beeinflusst und komprimierte Längen beobachtet. TLS verbirgt Inhalte, aber nicht jede Längeninformation. Nicht jede Längenänderung ist ein Angriff, doch eine gute Kompressionsrate ist kein Sicherheitsbeleg.

Die Darstellung „niemals indexiert“ verhindert die Aufnahme eines Wertes und muss von einem Vermittler beim Neukodieren bewahrt werden. Sie mindert ein bestimmtes Risiko, macht einen leicht erratbaren Wert aber nicht geheim und ersetzt weder Protokolltrennung noch Berechtigung.

Zwei Nachweise mit getrennten Schlüsseln

Der Kompressionsnachweis hält Verbindungskennung, Endpunktrollen, Richtung, Stream und Feldblockreihenfolge fest. Dazu kommen angekündigte Grenze, Bestätigung, wirksame Größe, Einfügungen, Verdrängungen, Größenänderungen, Darstellungsart und Dekodierergebnis.

Der HTTP-Nachweis beginnt nach der Rekonstruktion: Methode, URI, Status, Ursprungspfad und ausgelegte Semantik. Bei Cache-Nutzung dokumentiert er Cache-Schlüssel, Vary-Werte, Speicherentscheidung, Alter, Frische, Validatoren, Berechtigungsregeln, Revalidierung und Anwendungsergebnis.

Ein Paketmitschnitt kann synchrones HPACK beweisen und vom Cache nichts wissen. Ein Cache-Protokoll kann Wiederverwendung beweisen und die Drahtkompression ausblenden. Fehlende Spalten bleiben leer. Dass ein Decoder Oktette erinnerte, erlaubt noch keine Wiederverwendung einer Antwort.

Quellen