Jaki jest najdokładniejszy sposób odzyskania poprawnego adresu IP użytkownika w PHP?

301

Wiem, że istnieje mnóstwo $ _SERVER nagłówków zmiennych dostępnych do wyszukiwania adresów IP. Zastanawiałem się, czy istnieje ogólna zgoda co do tego, jak najdokładniej uzyskać prawdziwy adres IP użytkownika (dobrze wiedząc, że żadna metoda nie jest idealna) przy użyciu tych zmiennych?

Spędziłem trochę czasu próbując znaleźć dogłębne rozwiązanie i opracowałem następujący kod oparty na wielu źródłach. Bardzo bym chciała, gdyby ktoś mógł wywiercić dziurę w odpowiedzi lub rzucić nieco światła na coś być może bardziej dokładnego.

edycja obejmuje optymalizacje z @Alix

 /**
  * Retrieves the best guess of the client's actual IP address.
  * Takes into account numerous HTTP proxy headers due to variations
  * in how different ISPs handle IP addresses in headers between hops.
  */
 public function get_ip_address() {
  // Check for shared internet/ISP IP
  if (!empty($_SERVER['HTTP_CLIENT_IP']) && $this->validate_ip($_SERVER['HTTP_CLIENT_IP']))
   return $_SERVER['HTTP_CLIENT_IP'];

  // Check for IPs passing through proxies
  if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
   // Check if multiple IP addresses exist in var
    $iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
    foreach ($iplist as $ip) {
     if ($this->validate_ip($ip))
      return $ip;
    }
   }
  }
  if (!empty($_SERVER['HTTP_X_FORWARDED']) && $this->validate_ip($_SERVER['HTTP_X_FORWARDED']))
   return $_SERVER['HTTP_X_FORWARDED'];
  if (!empty($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']) && $this->validate_ip($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']))
   return $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];
  if (!empty($_SERVER['HTTP_FORWARDED_FOR']) && $this->validate_ip($_SERVER['HTTP_FORWARDED_FOR']))
   return $_SERVER['HTTP_FORWARDED_FOR'];
  if (!empty($_SERVER['HTTP_FORWARDED']) && $this->validate_ip($_SERVER['HTTP_FORWARDED']))
   return $_SERVER['HTTP_FORWARDED'];

  // Return unreliable IP address since all else failed
  return $_SERVER['REMOTE_ADDR'];
 }

 /**
  * Ensures an IP address is both a valid IP address and does not fall within
  * a private network range.
  *
  * @access public
  * @param string $ip
  */
 public function validate_ip($ip) {
     if (filter_var($ip, FILTER_VALIDATE_IP, 
                         FILTER_FLAG_IPV4 | 
                         FILTER_FLAG_IPV6 |
                         FILTER_FLAG_NO_PRIV_RANGE | 
                         FILTER_FLAG_NO_RES_RANGE) === false)
         return false;
     self::$ip = $ip;
     return true;
 }

Words of Warning (aktualizacja)

REMOTE_ADDRnadal stanowi najbardziej niezawodne źródło adresu IP. Inne $_SERVERwymienione tutaj zmienne mogą być bardzo łatwo sfałszowane przez zdalnego klienta. Celem tego rozwiązania jest próba ustalenia adresu IP klienta siedzącego za serwerem proxy. Do celów ogólnych możesz rozważyć użycie tego w połączeniu z adresem IP zwróconym bezpośrednio z $_SERVER['REMOTE_ADDR']i zapisaniem obu.

Dla 99,9% użytkowników to rozwiązanie idealnie spełni Twoje potrzeby. Nie ochroni cię przed 0,1% złośliwych użytkowników, którzy chcą nadużyć twojego systemu poprzez wstrzyknięcie własnych nagłówków żądań. Jeśli polegasz na adresach IP w celach krytycznych dla misji, REMOTE_ADDRuciekaj się do osób stojących za serwerem proxy i nie zawracaj sobie tym głowy.

Corey Ballou
źródło
2
W przypadku pytania whatismyip.com myślę, że robią coś takiego jak ten skrypt, czy uruchamiasz go lokalnie? Jeśli tak, to dlatego masz wewnętrzny adres IP, w tym przypadku nic nie jest wysyłane przez interfejs publiczny, więc nie ma informacji do uzyskania przez php
Matt
2
Pamiętaj o tym podczas wdrażania: stackoverflow.com/questions/1672827/…
Kevin Peno
19
Pamiętaj, że wszystkie te nagłówki HTTP są naprawdę łatwe do zmodyfikowania: wystarczy, że skonfigurujesz przeglądarkę tak, aby wysyłała nagłówek X-Forwarded-For z losowym adresem IP, a Twój skrypt chętnie zwróci fałszywy adres. Dlatego w zależności od tego, co próbujesz zrobić, to rozwiązanie może być mniej niezawodne niż zwykłe użycie REMOTE_ADDR.
gnomnain
14
OMFG, „zawodny ip”! Po raz pierwszy widzę taki nonsens tutaj na SO. Jedyny niezawodny adres IP to REMOTE_ADDR
Your Common Sense
3
-1 jest to podatne na fałszowanie. Wszystko, co robisz, to pytasz użytkownika, jaki powinien być jego adres IP.
wieża

Odpowiedzi:

268

Oto krótszy, czystszy sposób na uzyskanie adresu IP:

function get_ip_address(){
    foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key){
        if (array_key_exists($key, $_SERVER) === true){
            foreach (explode(',', $_SERVER[$key]) as $ip){
                $ip = trim($ip); // just to be safe

                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false){
                    return $ip;
                }
            }
        }
    }
}

Mam nadzieję, że to pomoże!


Twój kod wydaje się już dość kompletny, nie widzę w nim żadnych możliwych błędów (poza zwykłymi zastrzeżeniami IP), zmieniłbym validate_ip()funkcję, aby polegała na rozszerzeniu filtra:

public function validate_ip($ip)
{
    if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false)
    {
        return false;
    }

    self::$ip = sprintf('%u', ip2long($ip)); // you seem to want this

    return true;
}

Można także HTTP_X_FORWARDED_FORuprościć urywek:

// check for IPs passing through proxies
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
    // check if multiple ips exist in var
    if (strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ',') !== false)
    {
        $iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);

        foreach ($iplist as $ip)
        {
            if ($this->validate_ip($ip))
                return $ip;
        }
    }

    else
    {
        if ($this->validate_ip($_SERVER['HTTP_X_FORWARDED_FOR']))
            return $_SERVER['HTTP_X_FORWARDED_FOR'];
    }
}

Do tego:

// check for IPs passing through proxies
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
    $iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);

    foreach ($iplist as $ip)
    {
        if ($this->validate_ip($ip))
            return $ip;
    }
}

Możesz także sprawdzić poprawność adresów IPv6.

Alix Axel
źródło
4
Zdecydowanie doceniam tę filter_varpoprawkę, ponieważ usuwa ona kilka hackerskich, niepodpisanych kontroli int na adresie IP. Podoba mi się również fakt, że daje mi możliwość sprawdzania adresów IPv6. Bardzo HTTP_X_FORWARDED_FORdoceniana jest również optymalizacja. Za kilka minut zaktualizuję kod.
Corey Ballou
33
-1 to jest podatne na fałszowanie, tylko pytasz użytkownika, jaki powinien być jego adres IP.
wieża
7
@Rook: Tak, wiem. OP jest tego świadomy i wspomniałem o tym również w mojej odpowiedzi. Ale dzięki za komentarz.
Alix Axel
1
FYI: Musiałem usunąć FILTER_FLAG_IPV6, aby kod Alix Axel działał.
darkAsPitch 27.09.11
2
@ rubenrp81 Program obsługi gniazd TCP jest jedynym kanonicznym źródłem, wszystko inne jest kontrolowane przez atakującego. Powyższy kod to marzenie atakującego.
wieża
12

Jednak nawet wtedy uzyskanie prawdziwego adresu IP użytkownika będzie niewiarygodne. Wszystko, co muszą zrobić, to użyć anonimowego serwera proxy (takiego, który nie honoruje nagłówków http_x_forwarded_for, http_forwarded itp.), A wszystko, co otrzymasz, to adres IP ich serwera proxy.

Następnie możesz sprawdzić, czy istnieje lista anonimowych adresów IP serwera proxy, ale nie ma sposobu, aby upewnić się, że jest również w 100% dokładny, a wystarczy, że dowiesz się, że jest to serwer proxy. A jeśli ktoś jest sprytny, może sfałszować nagłówki dla przekazywania HTTP.

Powiedzmy, że nie lubię lokalnej uczelni. Sprawdzam, jakie adresy IP zarejestrowali, i zbanowałem ich adres w Twojej witrynie, robiąc złe rzeczy, ponieważ uważam, że honorujesz przekazywanie HTTP. Lista jest nieskończona.

Są też, jak się domyślacie, wewnętrzne adresy IP, takie jak sieć uczelni, o której wspominałem wcześniej. Wiele używa formatu 10.xxx. Więc wszystko, co wiesz, to że został przekazany do wspólnej sieci.

Wtedy nie zacznę od tego wiele, ale dynamiczne adresy IP są już drogą szerokopasmowego Internetu. Więc. Nawet jeśli otrzymasz adres IP użytkownika, spodziewaj się, że zmieni się on najpóźniej za 2-3 miesiące.

Peter Mortensen
źródło
Dzięki za wkład. Obecnie używam adresu IP użytkownika do pomocy w uwierzytelnianiu sesji, używając adresu IP jego klasy C jako czynnika ograniczającego ograniczenie przechwytywania sesji, ale pozwalającego na dynamiczne adresy IP w rozsądnym zakresie. Sfałszowane adresy IP i anonimowe serwery proxy to coś, z czym będę musiał się zmierzyć dla wybranej grupy osób.
Corey Ballou
@cballou - Z pewnością do tego celu służy REMOTE_ADDR. Każde podejście oparte na nagłówkach HTTP jest podatne na fałszowanie nagłówków. Jak długo trwa sesja? Dynamiczne adresy IP nie zmieniają się szybko.
MZB
Robią to, zwłaszcza jeśli chcę, aby to zrobili (zmień adres mac, który obsługuje wiele sterowników). Sam REMOTE_ADDR wystarczy, aby uzyskać dostęp do ostatniego serwera, z którym rozmawiał. Więc w sytuacji proxy dostajesz adres IP proxy.
8

Używamy:

/**
 * Get the customer's IP address.
 *
 * @return string
 */
public function getIpAddress() {
    if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
        return $_SERVER['HTTP_CLIENT_IP'];
    } else if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
        return trim($ips[count($ips) - 1]);
    } else {
        return $_SERVER['REMOTE_ADDR'];
    }
}

Eksplozja na HTTP_X_FORWARDED_FOR jest spowodowana dziwnymi problemami, które mieliśmy podczas wykrywania adresów IP, gdy używano Squid .

gabrielk
źródło
Ups, właśnie zdałem sobie sprawę, że robisz w zasadzie to samo z wybuchem i tak dalej. Plus trochę więcej. Więc wątpię, by moja odpowiedź była bardzo pomocna. :)
gabrielk
Zwraca adres hosta lokalnego
Scarl
3

Moja odpowiedź jest po prostu dopracowaną, w pełni sprawdzoną i w pełni spakowaną wersją odpowiedzi @ AlixAxel:

<?php

/* Get the 'best known' client IP. */

if (!function_exists('getClientIP'))
    {
        function getClientIP()
            {
                if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) 
                    {
                        $_SERVER['REMOTE_ADDR'] = $_SERVER["HTTP_CF_CONNECTING_IP"];
                    };

                foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key)
                    {
                        if (array_key_exists($key, $_SERVER)) 
                            {
                                foreach (explode(',', $_SERVER[$key]) as $ip)
                                    {
                                        $ip = trim($ip);

                                        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false)
                                            {
                                                return $ip;
                                            };
                                    };
                            };
                    };

                return false;
            };
    };

$best_known_ip = getClientIP();

if(!empty($best_known_ip))
    {
        $ip = $clients_ip = $client_ip = $client_IP = $best_known_ip;
    }
else
    {
        $ip = $clients_ip = $client_ip = $client_IP = $best_known_ip = '';
    };

?>

Zmiany:

  • Upraszcza nazwę funkcji (w stylu formatowania „camelCase”).

  • Obejmuje sprawdzenie, czy funkcja nie jest jeszcze zadeklarowana w innej części kodu.

  • Uwzględnia zgodność z „CloudFlare”.

  • Inicjuje wiele nazw zmiennych „związanych z IP” na zwróconą wartość funkcji „getClientIP”.

  • Zapewnia to, że jeśli funkcja nie zwróci prawidłowego adresu IP, wszystkie zmienne zostaną ustawione na pusty ciąg zamiast null.

  • To tylko (45) linii kodu.

James Anderson Jr.
źródło
2

Największe pytanie brzmi w jakim celu?

Twój kod jest prawie tak wszechstronny, jak to tylko możliwe - ale widzę, że jeśli zauważysz coś, co wygląda jak nagłówek dodany przez proxy, użyjesz tego ZAMIAST KLIENTA_IP, jednak jeśli chcesz, aby te informacje do celów audytu zostały ostrzeżone - to bardzo łatwe udawać.

Z pewnością nigdy nie należy używać adresów IP do jakiegokolwiek uwierzytelnienia - nawet one mogą zostać sfałszowane.

Lepszy pomiar adresu IP klienta można uzyskać, wypychając aplet Flash lub Java, który łączy się z serwerem za pośrednictwem portu innego niż http (co ujawniłoby przezroczyste proxy lub przypadki, w których nagłówki wstrzykiwane przez proxy są fałszywe - ale pamiętaj, że tam, gdzie klient może łączyć się TYLKO za pośrednictwem serwera proxy sieci Web lub port wychodzący jest zablokowany, połączenie z apletu nie będzie możliwe.

DO.

symcbean
źródło
Biorąc pod uwagę, że szukam rozwiązania opartego wyłącznie na języku PHP, sugerujesz dodanie $_SERVER['CLIENT_IP']jako drugiej instrukcji if?
Corey Ballou,
Nie - wystarczy, że jeśli chcesz przywiązać jakiekolwiek znaczenie do zwracanych danych, dobrym pomysłem byłoby zachowanie adresu punktu końcowego sieci (adresu IP klienta), a także wszelkich elementów sugerujących inną wartość w nagłówkach dodanych przez proxy (np. Możesz zobacz wiele adresów 192.168.1.x, ale pochodzących z różnych klientów ips) C.
symcbean
1

zdaję sobie sprawę, że powyżej są o wiele lepsze i bardziej zwięzłe odpowiedzi, i nie jest to funkcja ani najbardziej wdzięczny skrypt. W naszym przypadku musieliśmy wypisać zarówno sfałszowany x_forwarded_for, jak i bardziej niezawodny remote_addr w uproszczonym przełączniku na powiedz. Trzeba było pozwolić na spacje do wstrzykiwania do innych funkcji, jeśli-brak lub jeśli-liczba pojedyncza (zamiast tylko zwracania wstępnie sformatowanej funkcji). Potrzebował opcji „włączania lub wyłączania” z niestandardowymi etykietami dla ustawień przełącznika. Potrzebował również sposobu, aby $ ip był dynamiczny w zależności od żądania, aby przyjął postać forwarded_for.

Nie widziałem też nikogo, kto ma adres isset () vs! Empty () - nie można nic wpisać x_forwarded_for, ale nadal wyzwala prawdę isset (), co powoduje puste var, sposobem na obejście jest użycie && i połączenie obu jako warunków. Pamiętaj, że możesz fałszować słowa takie jak „PWNED” jako x_forwarded_for, więc upewnij się, że sterylizujesz do prawdziwej składni ip, jeśli wysyłasz dane gdzieś chronione lub do DB.

Ponadto możesz przetestować za pomocą translatora google, jeśli potrzebujesz wielu serwerów proxy, aby zobaczyć tablicę w x_forwarder_for. Jeśli chcesz sfałszować nagłówki do przetestowania, sprawdź to rozszerzenie Spoof nagłówka klienta Chrome . Będzie to domyślnie tylko standardowy remote_addr, gdy znajduje się za anon proxy.

Nie znam żadnego przypadku, w którym remote_addr mógłby być pusty, ale jest na wszelki wypadek rezerwowy.

// proxybuster - attempts to un-hide originating IP if [reverse]proxy provides methods to do so
  $enableProxyBust = true;

if (($enableProxyBust == true) && (isset($_SERVER['REMOTE_ADDR'])) && (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) && (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))) {
    $ip = end(array_values(array_filter(explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']))));
    $ipProxy = $_SERVER['REMOTE_ADDR'];
    $ipProxy_label = ' behind proxy ';
} elseif (($enableProxyBust == true) && (isset($_SERVER['REMOTE_ADDR']))) {
    $ip = $_SERVER['REMOTE_ADDR'];
    $ipProxy = '';
    $ipProxy_label = ' no proxy ';
} elseif (($enableProxyBust == false) && (isset($_SERVER['REMOTE_ADDR']))) {
    $ip = $_SERVER['REMOTE_ADDR'];
    $ipProxy = '';
    $ipProxy_label = '';
} else {
    $ip = '';
    $ipProxy = '';
    $ipProxy_label = '';
}

Aby uczynić je dynamicznymi do użycia w funkcjach lub zapytaniach / echu / widokach poniżej, powiedz do generowania logów lub raportowania błędów, używaj globałów lub po prostu wyświetlaj je gdziekolwiek chcesz, bez robienia wielu innych warunków lub generowania schematu statycznego Funkcje.

function fooNow() {
    global $ip, $ipProxy, $ipProxy_label;
    // begin this actions such as log, error, query, or report
}

Dziękuję za wszystkie twoje wspaniałe myśli. Daj mi znać, czy to może być lepsze, wciąż trochę nowe w tych nagłówkach :)

dhaupin
źródło
1

Wymyśliłem tę funkcję, która nie zwraca po prostu adresu IP, ale tablicę z informacjami IP.

// Example usage:
$info = ip_info();
if ( $info->proxy ) {
    echo 'Your IP is ' . $info->ip;
} else {
    echo 'Your IP is ' . $info->ip . ' and your proxy is ' . $info->proxy_ip;
}

Oto funkcja:

/**
 * Retrieves the best guess of the client's actual IP address.
 * Takes into account numerous HTTP proxy headers due to variations
 * in how different ISPs handle IP addresses in headers between hops.
 *
 * @since 1.1.3
 *
 * @return object {
 *         IP Address details
 *
 *         string $ip The users IP address (might be spoofed, if $proxy is true)
 *         bool $proxy True, if a proxy was detected
 *         string $proxy_id The proxy-server IP address
 * }
 */
function ip_info() {
    $result = (object) array(
        'ip' => $_SERVER['REMOTE_ADDR'],
        'proxy' => false,
        'proxy_ip' => '',
    );

    /*
     * This code tries to bypass a proxy and get the actual IP address of
     * the visitor behind the proxy.
     * Warning: These values might be spoofed!
     */
    $ip_fields = array(
        'HTTP_CLIENT_IP',
        'HTTP_X_FORWARDED_FOR',
        'HTTP_X_FORWARDED',
        'HTTP_X_CLUSTER_CLIENT_IP',
        'HTTP_FORWARDED_FOR',
        'HTTP_FORWARDED',
        'REMOTE_ADDR',
    );
    foreach ( $ip_fields as $key ) {
        if ( array_key_exists( $key, $_SERVER ) === true ) {
            foreach ( explode( ',', $_SERVER[$key] ) as $ip ) {
                $ip = trim( $ip );

                if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) !== false ) {
                    $forwarded = $ip;
                    break 2;
                }
            }
        }
    }

    // If we found a different IP address then REMOTE_ADDR then it's a proxy!
    if ( $forwarded != $result->ip ) {
        $result->proxy = true;
        $result->proxy_ip = $result->ip;
        $result->ip = $forwarded;
    }

    return $result;
}
Philipp
źródło
1

Jak ktoś powiedział wcześniej, kluczem tutaj jest powód, dla którego chcesz przechowywać ips użytkownika.

Podam przykład z systemu rejestracji, nad którym pracuję, i oczywiście rozwiązanie, aby przyczynić się do czegoś w tej starej dyskusji, która często pojawia się w moich poszukiwaniach.

Wiele bibliotek rejestracyjnych php używa ip do dławienia / blokowania nieudanych prób opartych na ip użytkownika. Rozważ tę tabelę:

-- mysql
DROP TABLE IF EXISTS `attempts`;
CREATE TABLE `attempts` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `ip` varchar(39) NOT NULL, /*<<=====*/
  `expiredate` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 -- sqlite
...

Następnie, gdy użytkownik próbuje zalogować się lub coś związanego z obsługą, np. Zresetować hasło, funkcja jest wywoływana na początku:

public function isBlocked() {
      /*
       * used one of the above methods to capture user's ip!!!
       */
      $ip = $this->ip;
      // delete attempts from this ip with 'expiredate' in the past
      $this->deleteAttempts($ip, false);
      $query = $this->dbh->prepare("SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ?");
      $query->execute(array($ip));
      $attempts = $query->fetchColumn();
      if ($attempts < intval($this->token->get('attempts_before_verify'))) {
         return "allow";
      }
      if ($attempts < intval($this->token->get('attempts_before_ban'))) {
         return "captcha";
      }
      return "block";
   }

Powiedzmy na przykład, że $this->token->get('attempts_before_ban') === 102 użytkowników korzysta z tego samego IP, jak ma to miejsce w poprzednich kodach, w których nagłówki mogą być sfałszowane , a następnie po 5 próbach oba są blokowane ! Co gorsza, jeśli wszyscy pochodzą z tego samego serwera proxy, tylko pierwszych 10 użytkowników zostanie zalogowanych, a cała reszta zostanie zablokowana!

Najważniejsze jest to, że potrzebujemy unikalnego indeksu na stole attemptsi możemy go uzyskać z kombinacji takiej jak:

 `ip` varchar(39) NOT NULL,
 `jwt_load varchar(100) NOT NULL

skąd jwt_loadpochodzi plik cookie http zgodny z technologią tokenów WWW firmy Json , w której przechowujemy tylko zaszyfrowane dane, które powinny zawierać dowolną / unikalną wartość dla każdego użytkownika. Oczywiście żądanie należy zmienić na: "SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ? AND jwt_load = ?"a klasa powinna również zainicjować a private $jwt.

centurian
źródło
0

Zastanawiam się, czy może powinieneś iterować po rozłożonym HTTP_X_FORWARDED_FOR w odwrotnej kolejności, ponieważ z mojego doświadczenia wynika, że ​​adres IP użytkownika kończy się na końcu listy oddzielonej przecinkami, więc zaczynając od początku nagłówka, jesteś bardziej prawdopodobne, że zwróci adres IP jednego z serwerów proxy, co potencjalnie może nadal umożliwiać przechwycenie sesji, ponieważ wielu użytkowników może przejść przez ten serwer proxy.

Chris Withers
źródło
1
Po przeczytaniu strony wikipedii na HTTP_X_FORWARDED_FOR: en.wikipedia.org/wiki/X-Forwarded-For ... Widzę, że sugerowana kolejność jest od lewej do prawej, tak jak w twoim kodzie. Jednak z naszych dzienników widzę, że istnieje wiele przypadków, w których proxy nie szanuje tego na wolności, a adres IP, który chcesz sprawdzić, może znajdować się na obu końcach listy.
Chris Withers,
1
Lub pośrodku, jak by się stało, gdyby niektórzy pełnomocnicy przestrzegali kolejności od lewej do prawej, a inni nie.
Brilliand
0

Dzięki za to bardzo przydatne.

Pomogłoby to, gdyby kod był poprawny pod względem składniowym. Ponieważ jest {za dużo wokół linii 20. Obawiam się, że nikt tak naprawdę tego nie wypróbował.

Mogę być szalony, ale po wypróbowaniu go na kilku prawidłowych i niepoprawnych adresach, jedyna działająca funkcja validate_ip () działała:

    public function validate_ip($ip)
    {
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE) === false)
            return false;
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) === false)
            return false;
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false)
            return false;

        return true;
    }
Mark Boon
źródło
0

Oto zmodyfikowana wersja, jeśli korzystasz z usług warstwy buforowania CloudFlare

function getIP()
{
    $fields = array('HTTP_X_FORWARDED_FOR',
                    'REMOTE_ADDR',
                    'HTTP_CF_CONNECTING_IP',
                    'HTTP_X_CLUSTER_CLIENT_IP');

    foreach($fields as $f)
    {
        $tries = $_SERVER[$f];
        if (empty($tries))
            continue;
        $tries = explode(',',$tries);
        foreach($tries as $try)
        {
            $r = filter_var($try,
                            FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 |
                            FILTER_FLAG_NO_PRIV_RANGE |
                            FILTER_FLAG_NO_RES_RANGE);

            if ($r !== false)
            {
                return $try;
            }
        }
    }
    return false;
}
jmserra
źródło
0

Tylko wersja VB.NET odpowiedzi:

Private Function GetRequestIpAddress() As IPAddress
    Dim serverVariables = HttpContext.Current.Request.ServerVariables
    Dim headersKeysToCheck = {"HTTP_CLIENT_IP", _
                              "HTTP_X_FORWARDED_FOR", _
                              "HTTP_X_FORWARDED", _
                              "HTTP_X_CLUSTER_CLIENT_IP", _
                              "HTTP_FORWARDED_FOR", _
                              "HTTP_FORWARDED", _
                              "REMOTE_ADDR"}
    For Each thisHeaderKey In headersKeysToCheck
        Dim thisValue = serverVariables.Item(thisHeaderKey)
        If thisValue IsNot Nothing Then
            Dim validAddress As IPAddress = Nothing
            If IPAddress.TryParse(thisValue, validAddress) Then
                Return validAddress
            End If
        End If
    Next
    Return Nothing
End Function
Liczydło
źródło
3
W pytaniu jest tag „PHP”
luchaninov
0

Kolejny czysty sposób:

  function validateIp($var_ip){
    $ip = trim($var_ip);

    return (!empty($ip) &&
            $ip != '::1' &&
            $ip != '127.0.0.1' &&
            filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false)
            ? $ip : false;
  }

  function getClientIp() {
    $ip = @$this->validateIp($_SERVER['HTTP_CLIENT_IP']) ?:
          @$this->validateIp($_SERVER['HTTP_X_FORWARDED_FOR']) ?:
          @$this->validateIp($_SERVER['HTTP_X_FORWARDED']) ?:
          @$this->validateIp($_SERVER['HTTP_FORWARDED_FOR']) ?:
          @$this->validateIp($_SERVER['HTTP_FORWARDED']) ?:
          @$this->validateIp($_SERVER['REMOTE_ADDR']) ?:
          'LOCAL OR UNKNOWN ACCESS';

    return $ip;
  }
Liko
źródło
0

Od klasy Żądania Symfony https://github.com/symfony/symfony/blob/1bd125ec4a01220878b3dbc3ec3156b073996af9/src/Symfony/Component/HttpFoundation/Request.php

const HEADER_FORWARDED = 'forwarded';
const HEADER_CLIENT_IP = 'client_ip';
const HEADER_CLIENT_HOST = 'client_host';
const HEADER_CLIENT_PROTO = 'client_proto';
const HEADER_CLIENT_PORT = 'client_port';

/**
 * Names for headers that can be trusted when
 * using trusted proxies.
 *
 * The FORWARDED header is the standard as of rfc7239.
 *
 * The other headers are non-standard, but widely used
 * by popular reverse proxies (like Apache mod_proxy or Amazon EC2).
 */
protected static $trustedHeaders = array(
    self::HEADER_FORWARDED => 'FORWARDED',
    self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
    self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
    self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
    self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
);

/**
 * Returns the client IP addresses.
 *
 * In the returned array the most trusted IP address is first, and the
 * least trusted one last. The "real" client IP address is the last one,
 * but this is also the least trusted one. Trusted proxies are stripped.
 *
 * Use this method carefully; you should use getClientIp() instead.
 *
 * @return array The client IP addresses
 *
 * @see getClientIp()
 */
public function getClientIps()
{
    $clientIps = array();
    $ip = $this->server->get('REMOTE_ADDR');
    if (!$this->isFromTrustedProxy()) {
        return array($ip);
    }
    if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) {
        $forwardedHeader = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]);
        preg_match_all('{(for)=("?\[?)([a-z0-9\.:_\-/]*)}', $forwardedHeader, $matches);
        $clientIps = $matches[3];
    } elseif (self::$trustedHeaders[self::HEADER_CLIENT_IP] && $this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) {
        $clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP])));
    }
    $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from
    $firstTrustedIp = null;
    foreach ($clientIps as $key => $clientIp) {
        // Remove port (unfortunately, it does happen)
        if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) {
            $clientIps[$key] = $clientIp = $match[1];
        }
        if (!filter_var($clientIp, FILTER_VALIDATE_IP)) {
            unset($clientIps[$key]);
        }
        if (IpUtils::checkIp($clientIp, self::$trustedProxies)) {
            unset($clientIps[$key]);
            // Fallback to this when the client IP falls into the range of trusted proxies
            if (null ===  $firstTrustedIp) {
                $firstTrustedIp = $clientIp;
            }
        }
    }
    // Now the IP chain contains only untrusted proxies and the client IP
    return $clientIps ? array_reverse($clientIps) : array($firstTrustedIp);
}
Luchaninov
źródło
Niezdefiniowana właściwość: $ server
C47
0

Dziwię się, że nikt nie wspomniał o filter_input, więc oto odpowiedź Alixa Axela skrócona do jednej linii:

function get_ip_address(&$keys = ['HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'HTTP_CLIENT_IP', 'REMOTE_ADDR'])
{
    return empty($keys) || ($ip = filter_input(INPUT_SERVER, array_pop($keys), FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE))? $ip : get_ip_address($keys);
}
mattavatar
źródło
-1

Prawie odpowiedziałeś na swoje pytanie! :)

function getRealIpAddr() {
    if(!empty($_SERVER['HTTP_CLIENT_IP']))   //Check IP address from shared Internet
    {
        $IPaddress = $_SERVER['HTTP_CLIENT_IP'];
    }
    elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))   //To check IP address is passed from the proxy
    {
        $IPaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
    }
    else
    {
        $IPaddress = $_SERVER['REMOTE_ADDR'];
    }
    return $IPaddress;
}

Źródło

Alex Weber
źródło
-6
/**
 * Sanitizes IPv4 address according to Ilia Alshanetsky's book
 * "php|architect?s Guide to PHP Security", chapter 2, page 67.
 *
 * @param string $ip An IPv4 address
 */
public static function sanitizeIpAddress($ip = '')
{
if ($ip == '')
    {
    $rtnStr = '0.0.0.0';
    }
else
    {
    $rtnStr = long2ip(ip2long($ip));
    }

return $rtnStr;
}

//---------------------------------------------------

/**
 * Returns the sanitized HTTP_X_FORWARDED_FOR server variable.
 *
 */
public static function getXForwardedFor()
{
if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
    {
    $rtnStr = $_SERVER['HTTP_X_FORWARDED_FOR'];
    }
elseif (isset($HTTP_SERVER_VARS['HTTP_X_FORWARDED_FOR']))
    {
    $rtnStr = $HTTP_SERVER_VARS['HTTP_X_FORWARDED_FOR'];
    }
elseif (getenv('HTTP_X_FORWARDED_FOR'))
    {
    $rtnStr = getenv('HTTP_X_FORWARDED_FOR');
    }
else
    {
    $rtnStr = '';
    }

// Sanitize IPv4 address (Ilia Alshanetsky):
if ($rtnStr != '')
    {
    $rtnStr = explode(', ', $rtnStr);
    $rtnStr = self::sanitizeIpAddress($rtnStr[0]);
    }

return $rtnStr;
}

//---------------------------------------------------

/**
 * Returns the sanitized REMOTE_ADDR server variable.
 *
 */
public static function getRemoteAddr()
{
if (isset($_SERVER['REMOTE_ADDR']))
    {
    $rtnStr = $_SERVER['REMOTE_ADDR'];
    }
elseif (isset($HTTP_SERVER_VARS['REMOTE_ADDR']))
    {
    $rtnStr = $HTTP_SERVER_VARS['REMOTE_ADDR'];
    }
elseif (getenv('REMOTE_ADDR'))
    {
    $rtnStr = getenv('REMOTE_ADDR');
    }
else
    {
    $rtnStr = '';
    }

// Sanitize IPv4 address (Ilia Alshanetsky):
if ($rtnStr != '')
    {
    $rtnStr = explode(', ', $rtnStr);
    $rtnStr = self::sanitizeIpAddress($rtnStr[0]);
    }

return $rtnStr;
}

//---------------------------------------------------

/**
 * Returns the sanitized remote user and proxy IP addresses.
 *
 */
public static function getIpAndProxy()
{
$xForwarded = self::getXForwardedFor();
$remoteAddr = self::getRemoteAddr();

if ($xForwarded != '')
    {
    $ip    = $xForwarded;
    $proxy = $remoteAddr;
    }
else
    {
    $ip    = $remoteAddr;
    $proxy = '';
    }

return array($ip, $proxy);
}
Meketrefe
źródło