Peer gniazda czy nagłówek przekazywany: któremu adresowi ufać?
Peer gniazda i nagłówek przekazywany to różne dowody. Porównaj, co każdy z nich dowodzi, i ufaj nagłówkowi tylko przez łańcuch proxy, który kontrolujesz.
Żądanie przychodzące do Twojej aplikacji niesie dwa rodzaje dowodów adresu: peera, który otworzył połączenie sieciowe, oraz pole HTTP twierdzące adres wcześniejszego klienta. Odpowiadają one na różne pytania. Peer gniazda jest obserwowany, a nagłówek jest twierdzony przez tego, kto go napisał, i żaden z nich nie powinien po cichu zastępować drugiego w logu ani w regule dostępu. Poniższe porównanie trzyma je osobno, a następnie pokazuje jedyny układ, w którym nagłówek staje się użyteczny.
Opisz ścieżkę, zanim cokolwiek porównasz:
Client -> approved reverse proxy -> application originW tym układzie peer gniazda po stronie źródła to zwykle proxy, a nagłówek przekazywania może nieść twierdzenie proxy o kliencie. Jego wartość pochodzi z kontrolowanej relacji z tym proxy, a nie z nazwy nagłówka.
Dwa rodzaje dowodów adresu
Peer gniazda to fakt o jednym połączeniu TCP: coś ukończyło uścisk dłoni z tego adresu. To niekoniecznie osoba, urządzenie lub konto, na którym Ci zależy, ponieważ VPN lub wyjście Tor mogą legalnie być źródłem, które usługa obserwuje. Wciąż jest to jedyny adres, który sam host zweryfikował, i na tym polega cała jego wartość.
Pole przekazywania to metadane o łańcuchu. X-Forwarded-For, X-Real-IP i standardowe pole Forwarded może zapisać każdy klient, który dotrze do punktu końcowego odbijającego je, więc ich wiarygodność zależy wyłącznie od tego, komu wolno było je ustawić.
Trzymaj oba w kontekście żądania pod różnymi nazwami. Aplikacja, która nadpisuje peera nagłówkiem, traci zdolność późniejszego odtworzenia błędu zaufania, a to jest właśnie moment, w którym jest on potrzebny.
Nagłówek nigdy nie przekierowuje połączenia
Zapisanie adresu w polu przekazywania nie zmienia źródła połączenia TCP docierającego do serwera i nie zmienia routingu nigdzie na ścieżce. Zwykły routing VPN lub NAT źródłowy dostarcza ruch bez żadnego nagłówka HTTP. Podobnie brama tranzytowa nie przepisuje poprawnie chronionej wymiany HTTPS bez zmiany układu punktu końcowego TLS.
Odwrotny błąd jest równie częsty: HTTPS nie czyni prawdziwym nagłówka przekazywania dostarczonego przez klienta. Chroni transport fałszywego twierdzenia dokładnie tak samo jak prawdziwego.
Jeśli kolega proponuje zmianę nagłówka, aby naprawić problem routingu, diagnoza już poszła źle. Napraw ścieżkę albo napraw konfigurację proxy, która zapisuje nagłówek.
Nazwij proxy, któremu wolno twierdzić
Dwie reguły czynią pole przekazywania użytecznym i obie są konfiguracją, a nie kodem. Krawędź normalizuje lub odrzuca wszystko, co przychodzi z zewnątrz, zgodnie z własnym udokumentowanym zachowaniem dla tych pól. Następnie źródło bierze adres zastępczy ze ścieżki proxy, którą nazwałeś, i znikąd indziej. Druga reguła obowiązuje tylko tak długo, jak klienci nie mogą dotrzeć do źródła w inny sposób, ponieważ źródło odpowiadające zarówno na żądania bezpośrednie, jak i przez proxy, nie wie, któremu z nich służy.
Moduł NGINX rzeczywistego IP wyraźnie pokazuje podział. set_real_ip_from nazywa nadawców, których twierdzenie się liczy, a real_ip_header mówi, które pole je niesie; pierwszy wykonuje zaufanie, drugi tylko mówi, gdzie patrzeć.
set_real_ip_from 10.20.0.0/24;
real_ip_header X-Forwarded-For;
# real_ip_recursive off - take the rightmost value in the field
# real_ip_recursive on - walk the list back past every trusted address
Sama dyrektywa wyboru nagłówka wciąż niczego nie ustala co do tego, kto wysłał wartość.
Bezpieczeństwo transportu musi działać na całej drodze między komponentami, którym ufasz. Krawędź, która poprawnie normalizuje, traci tę wartość w chwili, gdy niechroniony przeskok niżej pozwala komuś innemu edytować twierdzenie w drodze.
Świadomie obsługuj łańcuchy z wieloma przeskokami
Kilka proxy wymaga jawnego modelu łańcucha. Nie bierz skrajnej lewej ani prawej wartości oddzielonej przecinkami tylko dlatego, że jakiś wpis na blogu tak zrobił; ustal, które z Twoich proxy dopisuje, które zastępuje, a które zachowuje pole wejściowe. NGINX na przykład może przejść listę wstecz względem swojego zbioru zaufanych i zatrzymać się na pierwszym adresie, którego nie posiada, i to jest reguła tego produktu, a nie prawo, którego przestrzega każdy framework. Czytaj dokumentację własnego proxy z tymi samymi dwoma pytaniami: którą wartość wybiera i którym nadawcom wierzy?
Zapisz łańcuch: kolejność przeskoków, który przeskok ustawia pole i który przeskok usuwa przychodzące kopie. Potem trzymaj tę notatkę obok konfiguracji proxy, nie tylko w zgłoszeniu. Następnie przetestuj to, wysyłając żądanie z sfałszowanym nagłówkiem przez zatwierdzony proces walidacji i potwierdzając, że źródło je ignoruje.
Gdy dodajesz przeskok, przejrzyj ten dokument. Nowy balansujący obciążenie wstawiony przed zaufanym proxy może zmienić to, który adres wygrywa, bez czyjejkolwiek edycji aplikacji.
Trzymaj obie obserwacje w logu
Tam, gdzie diagnostyka jest dozwolona, loguj peera połączenia i znormalizowany adres klienta jako dwa osobne pola, i dodawaj tylko te metadane przekazywania, których cel naprawdę potrzebuje. Następnie udowodnij wdrożenie dwoma żądaniami: jednym przez proxy, które powinno rozsądnie zapisać obie wartości, oraz jedną próbą bezpośrednio do źródła, która musi zostać odrzucona.
Pamiętaj, co oznacza ta para. Nagłówek dodaje kontekst dostarczony przez komponent, który wybrałeś, by mu ufać; peer pozostaje jedynym adresem, który Twój host naprawdę widział. Żaden z nich nie jest dowodem indywidualnej tożsamości, a projekt, który traktuje którykolwiek jako dowód, pomyli się co do kogoś.