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_ADDR
nadal stanowi najbardziej niezawodne źródło adresu IP. Inne $_SERVER
wymienione 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_ADDR
uciekaj się do osób stojących za serwerem proxy i nie zawracaj sobie tym głowy.
źródło
Odpowiedzi:
Oto krótszy, czystszy sposób na uzyskanie adresu 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:Można także
HTTP_X_FORWARDED_FOR
uprościć urywek:Do tego:
Możesz także sprawdzić poprawność adresów IPv6.
źródło
filter_var
poprawkę, 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. BardzoHTTP_X_FORWARDED_FOR
doceniana jest również optymalizacja. Za kilka minut zaktualizuję kod.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.
źródło
Używamy:
Eksplozja na HTTP_X_FORWARDED_FOR jest spowodowana dziwnymi problemami, które mieliśmy podczas wykrywania adresów IP, gdy używano Squid .
źródło
Moja odpowiedź jest po prostu dopracowaną, w pełni sprawdzoną i w pełni spakowaną wersją odpowiedzi @ AlixAxel:
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.
źródło
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.
źródło
$_SERVER['CLIENT_IP']
jako drugiej instrukcji if?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.
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.
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 :)
źródło
Wymyśliłem tę funkcję, która nie zwraca po prostu adresu IP, ale tablicę z informacjami IP.
Oto funkcja:
źródło
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ę:
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:
Powiedzmy na przykład, że
$this->token->get('attempts_before_ban') === 10
2 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
attempts
i możemy go uzyskać z kombinacji takiej jak:skąd
jwt_load
pochodzi 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ć aprivate $jwt
.źródło
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.
źródło
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:
źródło
Oto zmodyfikowana wersja, jeśli korzystasz z usług warstwy buforowania CloudFlare
źródło
Tylko wersja VB.NET odpowiedzi:
źródło
Kolejny czysty sposób:
źródło
Od klasy Żądania Symfony https://github.com/symfony/symfony/blob/1bd125ec4a01220878b3dbc3ec3156b073996af9/src/Symfony/Component/HttpFoundation/Request.php
źródło
Dziwię się, że nikt nie wspomniał o filter_input, więc oto odpowiedź Alixa Axela skrócona do jednej linii:
źródło
Prawie odpowiedziałeś na swoje pytanie! :)
Źródło
źródło
źródło