Ich weiß, dass es eine Vielzahl von $ _SERVER- Variablen-Headern gibt, die zum Abrufen von IP-Adressen verfügbar sind. Ich habe mich gefragt, ob es einen allgemeinen Konsens darüber gibt, wie die tatsächliche IP-Adresse eines Benutzers (wohl wissend, dass keine Methode perfekt ist) unter Verwendung dieser Variablen am genauesten abgerufen werden kann.
Ich habe einige Zeit damit verbracht, eine gründliche Lösung zu finden, und habe den folgenden Code basierend auf einer Reihe von Quellen gefunden. Ich würde es lieben, wenn jemand bitte Löcher in die Antwort stechen oder etwas Licht auf etwas vielleicht Genaueres werfen könnte.
Bearbeiten enthält Optimierungen von @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;
}
Warnhinweise (Update)
REMOTE_ADDR
stellt immer noch die zuverlässigste Quelle einer IP-Adresse dar. Die anderen $_SERVER
hier genannten Variablen können von einem Remote-Client sehr einfach gefälscht werden. Mit dieser Lösung soll versucht werden, die IP-Adresse eines Clients zu ermitteln, der sich hinter einem Proxy befindet. Für Ihre allgemeinen Zwecke können Sie dies in Kombination mit der IP-Adresse verwenden, die direkt von $_SERVER['REMOTE_ADDR']
beiden zurückgegeben wird, und beide speichern.
Für 99,9% der Benutzer wird diese Lösung perfekt zu Ihren Anforderungen passen. Es schützt Sie nicht vor 0,1% der böswilligen Benutzer, die Ihr System missbrauchen möchten, indem sie ihre eigenen Anforderungsheader einfügen. Wenn Sie sich für geschäftskritische Zwecke auf IP-Adressen verlassen, REMOTE_ADDR
wenden Sie sich an diejenigen, die hinter einem Proxy stehen , und kümmern Sie sich nicht darum.
quelle
Antworten:
Hier ist ein kürzerer, sauberer Weg, um die IP-Adresse zu erhalten:
Ich hoffe, es hilft!
Ihr Code scheint bereits ziemlich vollständig zu sein. Ich kann keine möglichen Fehler darin erkennen (abgesehen von den üblichen IP-Einschränkungen). Ich würde die
validate_ip()
Funktion jedoch ändern , um mich auf die Filtererweiterung zu verlassen:Auch Ihr
HTTP_X_FORWARDED_FOR
Snippet kann dadurch vereinfacht werden:Dazu:
Möglicherweise möchten Sie auch IPv6-Adressen überprüfen.
quelle
filter_var
Update auf jeden Fall, da es eine Reihe von hackigen, nicht signierten Int-Überprüfungen der IP-Adresse entfernt. Ich mag auch die Tatsache, dass ich die Möglichkeit habe, auch IPv6-Adressen zu validieren. DieHTTP_X_FORWARDED_FOR
Optimierung wird ebenfalls sehr geschätzt. In ein paar Minuten werde ich den Code aktualisieren.Selbst dann ist es unzuverlässig, die tatsächliche IP-Adresse eines Benutzers zu erhalten. Sie müssen lediglich einen anonymen Proxyserver verwenden (einer, der die Header für http_x_forwarded_for, http_forwarded usw. nicht berücksichtigt). Sie erhalten lediglich die IP-Adresse des Proxyservers.
Sie können dann sehen, ob es eine Liste anonymer Proxy-Server-IP-Adressen gibt, aber es gibt keine Möglichkeit, sicherzustellen, dass diese auch 100% genau ist, und Sie würden höchstens wissen lassen, dass es sich um einen Proxy-Server handelt. Und wenn jemand klug ist, kann er Header für HTTP-Weiterleitungen fälschen.
Nehmen wir an, ich mag das örtliche College nicht. Ich finde heraus, welche IP-Adressen sie registriert haben, und lasse ihre IP-Adresse auf Ihrer Website durch schlechte Dinge sperren, weil ich herausfinde, dass Sie die HTTP-Weiterleitungen einhalten. Die Liste ist endlos.
Dann gibt es, wie Sie vermutet haben, interne IP-Adressen wie das College-Netzwerk, das ich zuvor kennengelernt habe. Viele verwenden ein 10.xxx-Format. Sie wissen also nur, dass es für ein freigegebenes Netzwerk weitergeleitet wurde.
Dann werde ich nicht viel damit anfangen, aber dynamische IP-Adressen sind der Weg des Breitbandes mehr. So. Selbst wenn Sie eine Benutzer-IP-Adresse erhalten, sollten Sie damit rechnen, dass sich diese höchstens in 2 bis 3 Monaten ändert.
quelle
Wir gebrauchen:
Die Explosion bei HTTP_X_FORWARDED_FOR ist auf seltsame Probleme zurückzuführen, bei denen bei der Verwendung von Squid IP-Adressen erkannt wurden.
quelle
Meine Antwort ist im Grunde nur eine ausgefeilte, vollständig validierte und vollständig verpackte Version der Antwort von @ AlixAxel:
Änderungen:
Es vereinfacht den Funktionsnamen (mit dem Formatierungsstil 'camelCase').
Es enthält eine Überprüfung, um sicherzustellen, dass die Funktion nicht bereits in einem anderen Teil Ihres Codes deklariert ist.
Es berücksichtigt die CloudFlare-Kompatibilität.
Es initialisiert mehrere "IP-bezogene" Variablennamen mit dem zurückgegebenen Wert der Funktion 'getClientIP'.
Es stellt sicher, dass, wenn die Funktion keine gültige IP-Adresse zurückgibt, alle Variablen anstelle von auf eine leere Zeichenfolge gesetzt werden
null
.Es sind nur (45) Codezeilen.
quelle
Die größte Frage ist zu welchem Zweck?
Ihr Code ist fast so umfassend wie er sein könnte - aber ich sehe, dass Sie diesen STATT des CLIENT_IP verwenden, wenn Sie einen Header sehen, der wie ein Proxy hinzugefügt wurde. Wenn Sie diese Informationen jedoch für Überwachungszwecke wünschen, werden Sie gewarnt - es ist sehr einfach fälschen.
Natürlich sollten Sie niemals IP-Adressen für irgendeine Art von Authentifizierung verwenden - auch diese können gefälscht werden.
Sie könnten eine bessere Messung der Client-IP-Adresse erhalten, indem Sie ein Flash- oder Java-Applet herausschieben, das über einen Nicht-http-Port eine Verbindung zum Server herstellt (wodurch transparente Proxys oder Fälle angezeigt werden, in denen die Header mit Proxy-Injektion falsch sind - aber Beachten Sie, dass keine Verbindung vom Applet besteht, wenn der Client NUR eine Verbindung über einen Webproxy herstellen kann oder der ausgehende Port blockiert ist.
C.
quelle
$_SERVER['CLIENT_IP']
als zweite if-Anweisung hinzuzufügen ?Mir ist klar, dass es oben viel bessere und präzisere Antworten gibt, und dies ist weder eine Funktion noch das anmutigste Skript, das es gibt. In unserem Fall mussten wir sowohl das spoofable x_forwarded_for als auch das zuverlässigere remote_addr in einem vereinfachten Switch per say ausgeben. Es mussten Leerzeichen zum Einfügen in andere Funktionen zugelassen werden, wenn keine oder nur Singular (anstatt nur die vorformatierte Funktion zurückzugeben). Für die Plattformeinstellungen war eine "Ein oder Aus" -Vari mit einem pro Switch angepassten Etikett erforderlich. Außerdem musste $ ip je nach Anforderung dynamisch sein, damit es die Form von forwarded_for annehmen konnte.
Außerdem habe ich niemanden gesehen, der die Adresse isset () vs! Empty () adressiert - es ist möglich, nichts für x_forwarded_ einzugeben, um dennoch die Wahrheit isset () auszulösen, was zu einer leeren var führt. Eine Möglichkeit, sich fortzubewegen, besteht darin, && zu verwenden und beide als Bedingungen zu kombinieren. Denken Sie daran, dass Sie Wörter wie "PWNED" als x_forwarded_for fälschen können. Stellen Sie daher sicher, dass Sie auf eine echte IP-Syntax sterilisieren, wenn Ihre Ausgabe an einem geschützten Ort oder in der Datenbank erfolgt.
Sie können auch mit Google Übersetzer testen, ob Sie einen Multi-Proxy benötigen, um das Array in x_forwarder_for anzuzeigen. Wenn Sie zu testende Header fälschen möchten, lesen Sie die Chrome Client Header Spoof- Erweiterung. Dies wird standardmäßig nur standardmäßig remote_addr hinter einem anon-Proxy verwendet.
Ich weiß nicht, in welchem Fall remote_addr leer sein könnte, aber es ist nur für den Fall ein Fallback.
Um diese dynamisch für die Verwendung in den folgenden Funktionen oder Abfragen / Echos / Ansichten zu machen, z. B. für Protokoll- oder Fehlerberichte, verwenden Sie Globals oder geben Sie sie einfach an einer beliebigen Stelle ein, ohne eine Menge anderer Bedingungen oder statische Schemaausgaben auszugeben Funktionen.
Vielen Dank für all Ihre tollen Gedanken. Bitte lassen Sie mich wissen, ob dies besser sein könnte, noch ein bisschen neu in diesen Headern :)
quelle
Ich habe mir diese Funktion ausgedacht, die nicht einfach die IP-Adresse zurückgibt, sondern ein Array mit IP-Informationen.
Hier ist die Funktion:
quelle
Wie bereits erwähnt, ist der Schlüssel hier, aus welchem Grund Sie die IPs des Benutzers speichern möchten.
Ich werde ein Beispiel aus einem Registrierungssystem geben, an dem ich arbeite, und natürlich die Lösung, um etwas in diese alte Diskussion einzubringen, die bei meinen Suchen häufig vorkommt.
Viele PHP-Registrierungsbibliotheken verwenden IP , um fehlgeschlagene Versuche basierend auf der IP des Benutzers zu drosseln / zu sperren. Betrachten Sie diese Tabelle:
Wenn ein Benutzer dann versucht, sich anzumelden oder etwas zu tun, das mit der Wartung zusammenhängt, wie z. B. ein Zurücksetzen des Passworts, wird zu Beginn eine Funktion aufgerufen:
Nehmen wir zum Beispiel an,
$this->token->get('attempts_before_ban') === 10
2 Benutzer verwenden die gleichen IP-Adressen wie in den vorherigen Codes, in denen Header gefälscht werden können. Nach jeweils 5 Versuchen werden beide gesperrt . Schlimmer noch, wenn alle vom selben Proxy stammen, werden nur die ersten 10 Benutzer protokolliert und der Rest wird gesperrt!Entscheidend dabei ist, dass wir einen eindeutigen Index für die Tabelle benötigen
attempts
und ihn aus einer Kombination wie der folgenden erhalten können:woher
jwt_load
kommt von einem http-Cookie, das der json-Web-Token- Technologie folgt, in der wir nur die verschlüsselten Nutzdaten speichern, die für jeden Benutzer einen beliebigen / eindeutigen Wert enthalten sollten . Natürlich sollte die Anfrage geändert werden zu:"SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ? AND jwt_load = ?"
und die Klasse sollte auch a initiierenprivate $jwt
.quelle
Ich frage mich, ob Sie vielleicht das explodierte HTTP_X_FORWARDED_FOR in umgekehrter Reihenfolge durchlaufen sollten, da ich die Erfahrung gemacht habe, dass die IP-Adresse des Benutzers am Ende der durch Kommas getrennten Liste endet. Sie beginnen also am Anfang des Headers Es ist wahrscheinlicher, dass die IP-Adresse eines der zurückgegebenen Proxys abgerufen wird, wodurch möglicherweise noch Sitzungsentführungen möglich sind, da möglicherweise viele Benutzer über diesen Proxy kommen.
quelle
Danke dafür, sehr nützlich.
Es wäre jedoch hilfreich, wenn der Code syntaktisch korrekt wäre. Wie es ist, gibt es eine {zu viele um Zeile 20. Was ich befürchte, bedeutet, dass niemand dies tatsächlich ausprobiert hat.
Ich mag verrückt sein, aber nachdem ich es an einigen gültigen und ungültigen Adressen ausprobiert habe, funktionierte nur folgende Version von validate_ip ():
quelle
Hier ist eine modifizierte Version, wenn Sie CloudFlare- Caching-Layer-Dienste verwenden
quelle
Nur eine VB.NET- Version der Antwort:
quelle
Nur ein weiterer sauberer Weg:
quelle
Aus der Anforderungsklasse von Symfony https://github.com/symfony/symfony/blob/1bd125ec4a01220878b3dbc3ec3156b073996af9/src/Symfony/Component/HttpFoundation/Request.php
quelle
Ich bin überrascht, dass niemand filter_input erwähnt hat. Hier ist die Antwort von Alix Axel in einer Zeile zusammengefasst:
quelle
Sie haben so ziemlich Ihre eigene Frage beantwortet! :) :)
Quelle
quelle
quelle