Cybersicherheit

Client-IP-Adressen hinter einem Reverse Proxy - Erst der Verbindung vertrauen, dann dem Header

Client-IP-Adressen hinter einem Reverse Proxy - Erst der Verbindung vertrauen, dann dem Header

Client-IP-Adressen hinter einem Reverse Proxy - Erst der Verbindung vertrauen, dann dem Header

Eine Anwendung hinter einem Reverse Proxy steht vor einer unbequemen Wahl. Zeichnet sie nur den Netzwerk-Peer auf, scheinen möglicherweise alle Anfragen vom Proxy zu kommen. Akzeptiert sie dagegen einen Header wie X-Forwarded-For ohne Vertrauensrichtlinie, kann ein Client unter Umständen die Adresse bestimmen, die in Protokollen, Rate Limits oder Zugriffsregeln erscheint.

Die hilfreiche Frage lautet daher nicht einfach: „Welcher Header enthält die echte IP-Adresse?“ Sie lautet: Welcher Rechner hat diese Anfrage übermittelt, welche Rechner dürfen Forwarding-Informationen hinzufügen und wo endet die vertrauenswürdige Kette?

Dieser Artikel entwickelt dieses Modell für eine kleine Nginx-Bereitstellung. Die Konfiguration ist bewusst generisch. Proxy-Adressen, Netzwerkpfade und anbieterspezifische Header müssen zur tatsächlichen Architektur passen, statt blind kopiert zu werden.

Eine Verbindungsadresse und ein Header sind unterschiedliche Belege

Bei einer direkten Verbindung kann Nginx die Adresse des Peers beobachten, der den Socket geöffnet hat. Steht ein Reverse Proxy davor, wird dieser Proxy zum unmittelbaren Peer. Die ursprüngliche Client-Adresse ist auf derselben Netzwerkschicht nicht mehr verfügbar, weshalb Proxies sie üblicherweise in HTTP-Metadaten weitergeben.

X-Forwarded-For ist ein verbreiteter De-facto-Request-Header. Die IETF standardisierte das verwandte Feld Forwarded im RFC 7239, doch das ältere X-Feld ist weiterhin üblich. Keiner dieser Namen authentifiziert seinen Wert. Auch ein Browser, ein Skript, ein falsch konfigurierter Vermittler oder ein böswilliger Client kann Forwarding-Felder senden.

Aus diesem Unterschied folgt eine praktische Regel: Vertrauen beginnt bei der direkten Verbindung, nicht beim Text ganz links in einem Header. Forwarding-Metadaten werden erst dann nützlich, wenn der unmittelbare Peer ein erwarteter Proxy ist und dieser Proxy eine definierte Richtlinie zum Erstellen, Ersetzen oder Erweitern der Angaben besitzt.

Eine Proxy-Kette von der vertrauenswürdigen Seite lesen

Angenommen, eine Anfrage erreicht den Origin mit diesem Feld:

X-Forwarded-For: 198.51.100.23, 10.20.0.8

Die übliche Reihenfolge verläuft von der frühesten Adresse links zu späteren Proxies rechts. „Ganz links“ bedeutet jedoch nicht „verifiziert“. Ein Client könnte bereits einen Wert geliefert haben, bevor der erste kontrollierte Proxy etwas angehängt hat.

Für eine sicherheitsrelevante Entscheidung läuft die Auswertung in die andere Richtung. Zuerst wird beim Socket-Peer geprüft, ob er zu einem eng definierten vertrauenswürdigen Proxy gehört. Anschließend wird die Liste von rechts nach links durchlaufen, solange die Adressen vertrauenswürdig bleiben. Die erste Adresse außerhalb dieser Menge ist die nächstgelegene vertretbare Client-Adresse. Wie die MDN-Referenz zu X-Forwarded-For warnt, kann sie zu einem nicht vertrauenswürdigen Vermittler statt zum Gerät einer Person gehören. Dennoch markiert sie die Grenze, jenseits derer der Origin keine Grundlage für weitere Behauptungen hat.

Diese Grenze wird häufig auf zwei Arten beschrieben:

  • Eine feste Anzahl von Proxy-Hops ist einfach, aber anfällig, wenn Anfragen unterschiedlich lange Pfade nehmen können.
  • Eine ausdrückliche Liste vertrauenswürdiger Proxy-Adressen oder -Netze bildet wechselnde Pfade meist sicherer ab, sofern sie korrekt gehalten wird.

Eine private Adresse ist nicht automatisch vertrauenswürdig und eine öffentliche Adresse nicht automatisch feindlich. Vertrauen entsteht durch Kontrolle über Route und Proxy, nicht dadurch, dass eine Adresse vertraut aussieht.

Die Grenze mit Nginx Real IP ausdrücken

Das Real-IP-Modul von Nginx kann die effektive Client-Adresse durch einen von einem bekannten Proxy gelieferten Wert ersetzen. Das Modul ist nicht in jedem Upstream-Build standardmäßig aktiviert. Deshalb sollte vor der Nutzung seiner Direktiven das installierte Binary geprüft werden:

nginx -V 2>&1

Gesucht wird --with-http_realip_module; alternativ ist zu klären, wie das Betriebssystempaket das Modul bereitstellt. Der Debian-Host, auf dem die Syntax dieses Artikels validiert wurde, meldet Nginx 1.26.3 mit dieser Build-Option. Dieses lokale Ergebnis bestätigt nur, dass das Beispiel auf diesem Host geparst wird. Es sagt nichts über das Paket oder die Proxy-Topologie eines anderen Servers aus.

Angenommen, ein interner Load Balancer unter 10.20.0.10 ist der einzige vorgesehene Peer des Origins und pflegt X-Forwarded-For. Eine Ausgangsrichtlinie lautet:

set_real_ip_from 10.20.0.10;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

Jede Zeile hat eine eigene Aufgabe:

  • set_real_ip_from nennt eine Adresse oder ein CIDR-Netz, dessen Ersatzinformationen Nginx akzeptiert. Gemeint sein sollten tatsächlich kontrollierte Proxies, nicht alle Adressen über eine bequeme Regel wie 0.0.0.0/0.
  • real_ip_header wählt das Feld mit der Ersatzadresse. Der Nginx-Standard ist X-Real-IP; dieses Beispiel wählt ausdrücklich X-Forwarded-For.
  • real_ip_recursive on veranlasst Nginx, die gelieferte Kette rückwärts zu durchlaufen und die letzte nicht vertrauenswürdige Adresse auszuwählen, statt nur den letzten Wert zu übernehmen.

Nach erfolgreicher Verarbeitung enthält $remote_addr die effektive Adresse. Nginx bewahrt den ursprünglichen Verbindungs-Peer in $realip_remote_addr auf. Werden während eines Rollouts beide protokolliert, bleibt die Entscheidung sichtbar:

log_format proxy_path
    '$remote_addr via $realip_remote_addr "$request"';

access_log /var/log/nginx/access.log proxy_path;

Dieses Beispiel verwendet eine hypothetische private Adresse. Eine reale Konfiguration benötigt jeden legitimen unmittelbaren Proxy-Pfad, gegebenenfalls einschließlich IPv6, und keinen unbeteiligten Adressbereich.

Edge-Proxy und Origin haben unterschiedliche Aufgaben

Ist Nginx Clients direkt ausgesetzt, ist sein Socket-Peer bereits die beobachtbare Adresse. Nginx muss nicht allein dafür einem vom Client gelieferten Forwarding-Header vertrauen. Leitet dieser Edge-Nginx die Anfrage anschließend an einen weiteren Dienst weiter, kann das Proxy-Modul die Kette übermitteln:

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

Der nachgelagerte Dienst sollte diesem Feld nur vertrauen, weil sein direkter Peer genau diese kontrollierte Nginx-Instanz ist. Er sollte nicht unabhängig eine widersprüchliche Regel wie „immer den ersten durch Komma getrennten Wert nehmen“ anwenden. Eine Schicht sollte die effektive Adresse festlegen; spätere Schichten sollten diese Entscheidung unter derselben dokumentierten Grenze verwenden.

Ein CDN bildet eine andere Anordnung. Cloudflare dokumentiert beispielsweise, dass ein Origin normalerweise eine Cloudflare-Adresse sieht und die Besucheradresse in CF-Connecting-IP erhält. Die Nginx-Anleitung kombiniert dieses Feld mit den von Cloudflare veröffentlichten Proxy-Netzen. Diese Netze können sich ändern, daher ist eine aus einem alten Artikel kopierte statische Liste keine dauerhafte Konfiguration.

Header und Prüfung des Quellnetzes gehören zusammen. Würde CF-Connecting-IP von jedem Peer akzeptiert, könnte ein direkter Client einen beliebigen Wert behaupten. Aktuelle Provider-Netze zu vertrauen und den Origin-Zugriff zugleich auf diesen Provider zu beschränken, schafft eine deutlich klarere Grenze.

Direkter Origin-Zugriff ist ein separates Problem

Korrektes Header-Parsing zwingt Traffic nicht durch ein CDN, einen Load Balancer oder eine WAF. Bleibt die Origin-Adresse öffentlich erreichbar, kann ein Client die vorgesehene Edge umgehen. Nginx kann das Forwarding-Feld dieses nicht vertrauenswürdigen Peers ignorieren, doch Caching, Filterung und Missbrauchskontrollen an der Edge wurden trotzdem umgangen.

Ist das Design tatsächlich proxy-only, muss diese Eigenschaft an der Netzwerkgrenze durchgesetzt werden: Der Origin-Port wird nur für das kontrollierte Proxy-Netz oder aktuelle Provider-Netze freigegeben, während ein bewusst vorgesehener Administrationsweg erhalten bleibt. Der konkrete Firewall-Mechanismus liegt außerhalb des Artikelumfangs. Entscheidend ist, dass Netzwerkerreichbarkeit und Interpretation von HTTP-Headern zwei Kontrollen sind und einander nicht ersetzen.

Den Pfad einschließlich eines Spoofing-Versuchs prüfen

Vor dem Reload eines produktiven Dienstes sollten die vollständige Konfiguration betrachtet und ihre Syntax validiert werden:

sudo nginx -T
sudo nginx -t

Eine Syntaxprüfung kann das Vertrauensmodell nicht beweisen. Das Verhalten muss über jeden erwarteten Pfad getestet und im Protokoll mit beiden Adressen verglichen werden:

  1. Eine normale Anfrage durch den Proxy senden und die erwartete effektive Adresse sowie den Proxy-Peer bestätigen.
  2. Über den öffentlichen Pfad eine Anfrage mit einem absichtlich falschen X-Forwarded-For-Wert senden. Nicht vertrauenswürdige Eingaben auf der linken Seite dürfen nicht zur Sicherheitsadresse werden.
  3. Falls absichtlich ein direkter Testpfad existiert, denselben gefälschten Header direkt senden. Ein nicht vertrauenswürdiger Peer darf Nginx nicht zum Ersetzen seiner Socket-Adresse bringen.
  4. Die Prüfung für jeden gültigen Pfad wiederholen, einschließlich IPv4 und IPv6, falls beide unterstützt werden.
  5. Prüfen, was PHP oder ein anderer Upstream tatsächlich erhält; es darf nicht vorausgesetzt werden, dass dessen Framework dieselbe Proxy-Richtlinie wie Nginx verwendet.

Tests sollten eine Adresse und einen Endpunkt verwenden, die für diesen Zweck kontrolliert werden. Der einzige Administrationsweg darf nicht ausgesperrt werden; außerdem sollte eine bekannte funktionierende Konfiguration für den Rollback erhalten bleiben. Sobald das Verhalten verstanden ist, erfolgt der Reload über das etablierte sichere Verfahren der Site, statt einen erfolgreichen Syntaxcheck als Beweis für korrektes Routing zu behandeln.

Eine IP-Adresse nicht überbewerten

Eine vertretbare Client-Adresse kann Protokolle, Incident-Untersuchungen, grobe Missbrauchskontrollen und Eingaben für Rate Limits verbessern. Sie ist keine Authentifizierung. Carrier-Grade NAT, gemeinsam genutztes WLAN, VPNs, Privacy Relays, Mobilfunknetze und normale Neuzuweisungen schwächen jede Behauptung, eine Adresse identifiziere eine Person oder ein Konto.

IP-basierte Zugriffsregeln benötigen außerdem eine Fehlerrichtlinie. Fehlen vertrauenswürdige Proxy-Metadaten oder sind sie fehlerhaft, kann die stille Wahl eines anderen Headers zu einem Fail-open führen. Eine sensible Route muss je nach Architektur möglicherweise die Anfrage ablehnen oder auf den verifizierten Socket-Peer zurückfallen. Diese Entscheidung sollte ausdrücklich getroffen und getestet werden.

Hinzu kommen Datenschutzkosten. RFC 7239 behandelt weitergeleitete Adressen als potenziell sensibel und erörtert Informationslecks sowie Privatsphäre. Sie sollten nur für einen benannten Betriebszweck gespeichert, der Zugriff auf Protokolle beschränkt und eine Aufbewahrungsdauer festgelegt werden. Eine vollständige interne Proxy-Kette gehört weder in Antworten noch in unnötige Analysen.

Eine nützliche Adresse beginnt mit einer kleinen Vertrauensgrenze

Die sicherste Antwort auf „Wie lautet die echte Client-IP?“ ist häufig, dass das System sie nicht mit absoluter Gewissheit kennen kann. Es kann etwas Engeres wissen: Ein bestimmter vertrauenswürdiger Proxy hat eine Adresse beobachtet und sie über einen kontrollierten Pfad weitergegeben.

Auf dieser engen Aussage lässt sich aufbauen. Der unmittelbare Proxy wird identifiziert, nur seinen tatsächlichen Adressen wird vertraut, Multi-Hop-Informationen werden von der nächsten vertrauenswürdigen Seite ausgewertet und die direkte Origin-Erreichbarkeit wird mit dem Design in Einklang gebracht. Während der Prüfung werden die effektive Adresse und der ursprüngliche Peer protokolliert. Das Ergebnis bleibt anschließend ein Betriebssignal und wird nicht zum Identitätsnachweis erklärt.

References