- Als Proxy-Protokoll, das Anfragen per Socket an lang laufende Backends weiterreicht, lässt es sich einsetzen, ohne die bestehende HTTP-Handler-Struktur groß zu verändern
- HTTP/1.1-Reverse-Proxying neigt dazu, dass die Interpretation von Nachrichtenbegrenzungen je nach Implementierung auseinanderläuft, was weiterhin schwerwiegende Sicherheitsprobleme wie Desync und Request Smuggling verursachen kann
- FastCGI bietet seit 1996 ein klares Message Framing und trennt strukturell Client-Header von Vertrauensinformationen, die der Proxy hinzugefügt hat
- Gos
net/http/fcgifülltREMOTE_ADDRinRequest.RemoteAddrein und spiegelt auch den HTTPS-Status inRequest.TLSwider, sodass die Weitergabe von Vertrauensinformationen ohne zusätzliches Middleware-Setup funktioniert - Es gibt Einschränkungen wie fehlende WebSockets-Unterstützung, ein schwaches Tooling-Ökosystem und geringeren Durchsatz bei manchen Workloads, aber wenn keine WebSockets benötigt werden und die Leistung ausreicht, bleibt es eine praktikable Wahl
Die Rolle von FastCGI und wie es eingesetzt wird
- FastCGI wird nicht nur für das pro Datei ausgeführte Prozessmodell verwendet, sondern kann auch als Proxy-Backend-Protokoll dienen, bei dem Anfragen per TCP- oder UNIX-Socket an einen lang laufenden Daemon gesendet werden
- In Go reicht es, das Paket
net/http/fcgizu importieren undhttp.Servedurchfcgi.Servezu ersetzen- Bestehende Handler verwenden weiterhin
http.ResponseWriterundhttp.Request - Auch die übrige Struktur der Anwendung bleibt unverändert
- Bestehende Handler verwenden weiterhin
- Wichtige Proxys wie Apache, Caddy, nginx und HAProxy unterstützen FastCGI-Backends, und die Konfiguration ist vergleichsweise einfach
Parsing-Probleme bei HTTP als Backend-Protokoll
- HTTP-Reverse-Proxying kommt einem sicherheitstechnischen Minenfeld nahe, und es tauchen weiterhin Probleme auf, die wie die Desync-Schwachstelle im Discord-Media-Proxy private Anhänge ausspähen können
- HTTP/1.1 wirkt auf den ersten Blick wie ein einfaches textbasiertes Protokoll, bietet aber zu viele Möglichkeiten, dieselbe Nachricht darzustellen, und enthält viele Sonderfälle, sodass Implementierungen sie leicht unterschiedlich interpretieren
- Das größte Problem ist, dass HTTP-Nachrichten keine explizite Framing-Definition haben
- Das Ende einer Nachricht wird innerhalb der Nachricht selbst auf mehrere Arten beschrieben
- Je nach Implementierung kann unterschiedlich interpretiert werden, wo eine Nachricht endet und die nächste beginnt
- Solche Abweichungen bilden die Grundlage für HTTP-Desync-Angriffe beziehungsweise Request Smuggling und führen zu schweren Sicherheitsproblemen, wenn Reverse Proxy und Backend Nachrichtenbegrenzungen unterschiedlich verstehen
- Parser-Unterschiede immer weiter zu patchen, dürfte keine grundlegende Lösung sein
- James Kettle entdeckt weiterhin neue Varianten
- Nachdem er im vergangenen Jahr weitere Fälle gefunden hatte, verwendete er sogar die Formulierung "HTTP/1.1 must die"
Behandlung von Nachrichtenbegrenzungen in FastCGI und HTTP/2
- HTTP/2 kann Desync-Probleme lösen, wenn es zwischen Proxy und Backend konsistent verwendet wird, weil es Nachrichtenbegrenzungen klar definiert
- FastCGI bietet diese klare Trennung seit 1996 bereits mit einem einfacheren Protokoll
- nginx unterstützt FastCGI-Backends seit der ersten Veröffentlichung, aber HTTP/2-Backends wurden erst Ende 2025 hinzugefügt
- Die Unterstützung für HTTP/2-Backends in Apache befindet sich weiterhin im Status "experimental"
Das Problem unvertrauenswürdiger Header und FastCGIs Trennungsansatz
- Nicht nur bei Desync, auch für Daten, die ein Proxy vertrauenswürdig weiterreichen muss — etwa die tatsächliche Client-IP, den vom Proxy behandelten authentifizierten Benutzernamen oder Informationen zum Client-Zertifikat bei mTLS — fehlt HTTP ein robuster Transportmechanismus
- In der Praxis werden diese Informationen daher in HTTP-Header geschrieben, aber zwischen Vertrauensdaten, die der Proxy hinzugefügt hat, und unvertrauenswürdigen Headern vom Client gibt es keine strukturelle Trennung
- Header wie
X-Real-IPwerden häufig verwendet, um die echte Client-IP weiterzugeben, aber sicher ist das nur, wenn der Proxy zuvor alle vorhandenen Varianten dieses Headers einschließlich Groß-/Kleinschreibungsvarianten vollständig entfernt und ihn dann neu setzt - Dieser Ansatz ist extrem riskantes Terrain, und es gibt viele Wege, auf denen das Backend am Ende doch Daten vertraut, die ein Angreifer eingeschleust hat
- Der Proxy muss nicht nur
X-Real-IP, sondern jeden Header für solche Zwecke vollständig löschen - Zum Beispiel prüft Chi-Middleware bei der Bestimmung der tatsächlichen Client-IP zuerst
True-Client-IPund verwendetX-Real-IPnur, wenn dieser fehlt- Selbst wenn der Proxy
X-Real-IPkorrekt verarbeitet, kann ein Angreifer Probleme verursachen, indem erTrue-Client-IPmitsendet
- Selbst wenn der Proxy
- FastCGI trennt Client-Header und vom Proxy hinzugefügte Informationen über eine Art Domain Separation
- Beides wird zwar als Liste von Schlüssel/Wert-Parametern übertragen, aber HTTP-Headernamen erhalten das Präfix
HTTP_ - Dadurch kann ein vom Client gesendeter Header strukturell nicht als Vertrauensdatum des Proxys interpretiert werden
- Beides wird zwar als Liste von Schlüssel/Wert-Parametern übertragen, aber HTTP-Headernamen erhalten das Präfix
Umgang mit Vertrauensinformationen in Go über FastCGI
- FastCGI definiert Standardparameter wie
REMOTE_ADDR, um die tatsächliche Client-IP zu übermitteln - Gos
net/http/fcgiübernimmt diesen Wert automatisch inhttp.Request.RemoteAddr, sodass es ohne zusätzliche Middleware funktioniert - Der Proxy kann auch Informationen wie die Nutzung von HTTPS, die ausgehandelte TLS Cipher Suite oder das Client-Zertifikat über nicht standardisierte Parameter weitergeben
- Go setzt das
TLS-Feld vonRequestautomatisch auf einen nicht-nil-Wert, wenn die Anfrage HTTPS verwendet hat- Das ist selbst dann nützlich, wenn es leer ist, um zu prüfen, ob HTTPS erzwungen wurde
- Über
fcgi.ProcessEnvlässt sich auf den vollständigen Satz vertrauenswürdiger Parameter zugreifen, die der Proxy übermittelt hat
Warum die Verbreitung schleppend ist und welche praktischen Grenzen es gibt
- Wenn FastCGI besser ist, warum wird es dann nicht breit genutzt? Dazu tragen offenbar sowohl der veraltete Klang des Namens als auch ein mangelndes Bewusstsein für die Sicherheitsprobleme von HTTP-Reverse-Proxying bei
- Watchfire behandelte Desync-Angriffe bereits 2005 und warnte auch davor, dass sie sich nicht leicht beheben lassen, aber solche Angriffe bekamen über mehr als zehn Jahre hinweg kaum echte Aufmerksamkeit
- FastCGI ist auch heute noch praxistauglich, und bei SSLMate wird es seit über zehn Jahren in Produktion eingesetzt
- Allerdings hat diese alte Technologie auch Schwächen
- Sie wurde nicht für WebSockets-Unterstützung weiterentwickelt
- Das Tooling-Ökosystem ist schwach
- Zum Beispiel unterstützt curl sogar FTP, Gopher und SMTP, kann aber keine FastCGI-Anfragen senden
- Beim Benchmarking eines Go-FastCGI-Servers hinter mehreren Reverse Proxys zeigten einige Workloads weniger Durchsatz als HTTP/1.1 oder HTTP/2
- Das wird eher als Folge davon gesehen, dass der FastCGI-Codepfad nicht so stark optimiert ist wie HTTP, und nicht als grundsätzliche Grenze des Protokolls
Schlussurteil
- Wenn keine WebSockets benötigt werden und die aktuelle Leistung ausreicht, ist FastCGI weiterhin eine brauchbare Option
- Selbst wenn ein Flaschenhals entsteht, erscheint es sinnvoller, zusätzliche Hardware einzusetzen, als die Komplexität und den Sicherheitsalbtraum von HTTP-Reverse-Proxying in Kauf zu nehmen
Noch keine Kommentare.