2012-07-12 28 views
20

W porządku, mam mały problem z uwierzytelnianiem. Moja usługa internetowa pozwala połączyć się z moim API przez HTTP za pomocą nazwy użytkownika i hasła, ale to połączenie może być również ograniczone do określonego adresu IP.Jak poprawnie używać protokołu HTTP_X_FORWARDED_FOR?

Oznacza to, że $_SERVER['REMOTE_ADDR'] może być niepoprawny. Wiem już, że nigdy nie można polegać na żadnej informacji IP - mam ograniczenie tylko w celu dodania kolejnej warstwy bezpieczeństwa.

Jeśli jest to ogólny przegląd żądanie do serwera WWW:

clientSERVER => clientPROXY => myPROXY => mySERVER

oznacza to, że mój_serwer pokazuje REMOTE_ADDR z MyProxy zamiast tego klienta i wysyła rzeczywiste IP klienta jako HTTP_X_FORWARDED_FOR.

Aby temu zaradzić, moja usługa sieciowa ma listę adresów IP zaufanych serwerów proxy, a jeśli REMOTE_ADDR pochodzi z jednego z tych zaufanych adresów IP, to informuje moją usługę internetową, że rzeczywisty adres IP jest wartością HTTP_X_FORWARDED_FOR.

Problem dotyczy teraz clientPROXY. Oznacza to, że (dość często) mySERVER otrzymuje wartość HTTP_X_FORWARDED_FOR, która ma wiele adresów IP. Zgodnie z dokumentacją HTTP_X_FORWARDED_FOR, wartością jest rozdzielana przecinkami lista adresów IP, gdzie pierwszym adresem IP jest rzeczywisty prawdziwy klient, a każdy inny adres IP jest adresem proxy.

Tak więc, jeśli HTTP_X_FORWARDED_FOR ma wiele wartości, a moja usługa jest ograniczona do IP, czy muszę sprawdzić "ostatnią" wartość HTTP_X_FORWARDED_FOR na mojej dozwolonej liście adresów IP i po prostu zignorować rzeczywisty adres IP klienta?

Zakładam, że w systemie, w którym muszę ustawić listę dozwolonych adresów IP, adres IP na białej liście powinien być adresem serwera proxy, a nie adresem IP znajdującym się za serwerem proxy (ponieważ może to być lokalny adres IP i często się zmieniaj).

A co z HTTP_CLIENT_IP?

+0

http : //stackoverflow.com/q/7445592/759866 – Benjamin

Odpowiedz

-4

HTTP_CLIENT_IP to najbardziej niezawodny sposób na uzyskanie adresu IP użytkownika. Dalej jest HTTP_X_FORWARDED_FOR, a następnie REMOTE_ADDR. Sprawdź wszystkie trzy, w tej kolejności, zakładając, że pierwszy ustawiony (isset($_SERVER['HTTP_CLIENT_IP']) zwraca wartość true, jeśli ta zmienna jest ustawiona) jest poprawny. Możesz samodzielnie sprawdzić, czy użytkownik korzysta z serwera proxy przy użyciu różnych metod. Sprawdź this na zewnątrz.

+7

Żaden z tych nagłówków nie jest "bardziej niezawodny" niż pozostałe. Wszystkie mogą być sfałszowane przez klienta, a aplikacje muszą wiedzieć, co jest zaufane, a co nie. Zwykle znasz swoją infrastrukturę i adres IP dowolnego serwera proxy/load balancer przed serwerami HTTP. – Benjamin

+1

To prawda, ale ogólnie, jeśli kolejność preferencji zostanie ustalona na podstawie zakładanej niezawodności, to jest to. Zdaję sobie sprawę, że można nimi manipulować, ale jeśli trzeba je wykorzystać, tak to zrobić. – TheEnvironmentalist

+5

Dlaczego jest tak wiele dezinformacji na ten temat? '$ _SERVER ['REMOTE_ADDR']' to jedyne * niezawodne pole, na które nie ma wpływu użytkownik zdalny, wszystkie pozostałe są analizowane z nagłówków. – transistor09

13

Można użyć tej funkcji w celu uzyskania odpowiedniego adresu IP klient:

public function getClientIP(){  
    if (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)){ 
      return $_SERVER["HTTP_X_FORWARDED_FOR"]; 
    }else if (array_key_exists('REMOTE_ADDR', $_SERVER)) { 
      return $_SERVER["REMOTE_ADDR"]; 
    }else if (array_key_exists('HTTP_CLIENT_IP', $_SERVER)) { 
      return $_SERVER["HTTP_CLIENT_IP"]; 
    } 

    return ''; 
} 
+3

Ta odpowiedź nie dotyczy konkretnej sytuacji opisanej w pytaniu, gdzie ** nie wszystkie wnioski są przekazywane za pośrednictwem serwera proxy **. W rezultacie żądania otrzymane bezpośrednio od klientów mogą zawierać niedokładne adresy IP w nagłówkach. – duskwuff

+0

Dobra odpowiedź, możesz znaleźć nawet dokładną klasę Prestashop's Tools :). Znajdź funkcję getRemoteAddr() –

13

lubię odpowiedź Hrishikesh tych, do których mam tylko to, aby dodać ... bo widzieliśmy ciąg rozdzielany przecinkami napotykając po wielokrotność proxy po drodze były używane, okazało się, że konieczne dodaj eksplodować i chwycić ostateczną wartość, podobnie jak to:

$IParray=array_values(array_filter(explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']))); 
return end($IParray); 

array_filter tam jest usunięcie pustych wpisów.

+2

Należy zauważyć, że korzystanie z ostatniej wartości na liście nadal prawdopodobnie używa adresu IP pośrednika. Zgodnie z linkiem poniżej klient pierwotny jest PIERWSZYM IP. http://pl.wikipedia.org/wiki/X-Forwarded-For –

+2

Należy również zauważyć, że to samo źródło mówi, że jest to łatwe do podrobienia, więc ten ostatni jest bardziej niezawodny. Więc każdy przypadek użycia może dokonywać różnych wyborów. Jeśli przypadkiem użycia IP jest zwalczanie oszustw lub spamu, pierwszy adres IP może być bez znaczenia, a najbardziej niezawodny adres - ostatni - jest najbardziej użyteczny. Jeśli przypadkiem użycia w celu uzyskania IP jest mniej nikczemne działania, pierwszy byłby najbardziej użyteczny. –

0

Jeśli używamy go w bazie danych, jest to dobry sposób:

Ustaw pole IP w bazie danych do varchar (250), a następnie użyj tego:

$theip = $_SERVER["REMOTE_ADDR"]; 

if (!empty($_SERVER["HTTP_X_FORWARDED_FOR"])) { 
    $theip .= '('.$_SERVER["HTTP_X_FORWARDED_FOR"].')'; 
} 

if (!empty($_SERVER["HTTP_CLIENT_IP"])) { 
    $theip .= '('.$_SERVER["HTTP_CLIENT_IP"].')'; 
} 

$realip = substr($theip, 0, 250); 

Wtedy po prostu sprawdzić $ realip przeciwko polu IP bazy danych

6

W świetle najnowszych luk w zabezpieczeniach httpoxy, istnieje naprawdę potrzeba pełnego przykładu, jak prawidłowo używać HTTP_X_FORWARDED_FOR.

Oto przykład napisany w PHP, jak wykryć adres IP klienta, jeśli wiesz, że klient może być za proxy i wiesz, że ten serwer proxy może być zaufany. Jeśli nie wiadomo, żadnych zaufanych serwerów proxy, wystarczy użyć REMOTE_ADDR

<?php 

function get_client_ip() 
{ 
    // Nothing to do without any reliable information 
    if (!isset ($_SERVER['REMOTE_ADDR'])) { 
     return NULL; 
    } 

    // Header that is used by the trusted proxy to refer to 
    // the original IP 
    $proxy_header = "HTTP_X_FORWARDED_FOR"; 

    // List of all the proxies that are known to handle 'proxy_header' 
    // in known, safe manner 
    $trusted_proxies = array ("2001:db8::1", "192.168.50.1"); 

    if (in_array ($_SERVER['REMOTE_ADDR'], $trusted_proxies)) { 

     // Get the IP address of the client behind trusted proxy 
     if (array_key_exists ($proxy_header, $_SERVER)) { 

      // Header can contain multiple IP-s of proxies that are passed through. 
      // Only the IP added by the last proxy (last IP in the list) can be trusted. 
      $proxy_list = explode (",", $_SERVER[$proxy_header]); 
      $client_ip = trim (end ($proxy_list)); 

      // Validate just in case 
      if (filter_var ($client_ip, FILTER_VALIDATE_IP)) { 
       return $client_ip; 
      } else { 
       // Validation failed - beat the guy who configured the proxy or 
       // the guy who created the trusted proxy list? 
       // TODO: some error handling to notify about the need of punishment 
      } 
     } 
    } 

    // In all other cases, REMOTE_ADDR is the ONLY IP we can trust. 
    return $_SERVER['REMOTE_ADDR']; 
} 

print get_client_ip(); 

?> 
+0

To jest doskonała odpowiedź, ale jest jeden drobny problem. Jeśli włączone jest ścisłe zgłaszanie błędów, próba "przycinania (zakończenie (rozbicie()))" w jednym wierszu powróci "Tylko zmienne powinny być przekazywane przez odniesienie." Aby obejść ten problem, ustaw rozproszony nagłówek proxy na zmienną, a następnie zamiast tego użyj opcji trim (end()). –

+0

@BenDyer Dzięki, poprawiono. –

1

Można również rozwiązać ten problem za pomocą konfiguracji Apache przy użyciu mod_remoteip, poprzez dodanie następujących do pliku conf.d:

RemoteIPHeader X-Forwarded-For 
RemoteIPInternalProxy 172.16.0.0/12 
LogFormat "%a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined