Was ist der genaueste Weg, um die korrekte IP-Adresse eines Benutzers in PHP abzurufen?


301

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_ADDRstellt immer noch die zuverlässigste Quelle einer IP-Adresse dar. Die anderen $_SERVERhier 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_ADDRwenden Sie sich an diejenigen, die hinter einem Proxy stehen , und kümmern Sie sich nicht darum.


2
Für die Frage whatismyip.com denke ich, dass sie so etwas wie dieses Skript machen. Führen Sie es lokal aus? Wenn Sie aus diesem Grund eine interne IP haben, wird in diesem Fall nichts über die öffentliche Schnittstelle gesendet, sodass keine Informationen für PHP verfügbar sind
Matt


19
Denken Sie daran, dass all diese HTTP-Header sehr einfach zu ändern sind: Mit Ihrer Lösung muss ich nur meinen Browser so konfigurieren, dass ein X-Forwarded-For-Header mit einer zufälligen IP gesendet wird, und Ihr Skript gibt gerne eine gefälschte Adresse zurück. Je nachdem, was Sie versuchen, ist diese Lösung möglicherweise weniger zuverlässig als die einfache Verwendung von REMOTE_ADDR.
Gnomnain

14
OMFG, "unzuverlässige IP"! Zum ersten Mal sehe ich so einen Unsinn hier auf SO. Die einzige zuverlässige IP-Adresse ist REMOTE_ADDR
Ihr

3
-1 Dies ist anfällig für Spoofing. Sie fragen den Benutzer lediglich nach seiner IP-Adresse.
Turm

Antworten:


268

Hier ist ein kürzerer, sauberer Weg, um die IP-Adresse zu erhalten:

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;
                }
            }
        }
    }
}

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:

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;
}

Auch Ihr HTTP_X_FORWARDED_FORSnippet kann dadurch vereinfacht werden:

// 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'];
    }
}

Dazu:

// 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;
    }
}

Möglicherweise möchten Sie auch IPv6-Adressen überprüfen.


4
Ich schätze das filter_varUpdate 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. Die HTTP_X_FORWARDED_FOROptimierung wird ebenfalls sehr geschätzt. In ein paar Minuten werde ich den Code aktualisieren.
Corey Ballou

33
-1 Dies ist anfällig für Spoofing. Sie fragen den Benutzer lediglich nach seiner IP-Adresse.
Turm

7
@Rook: Ja, ich weiß. Das OP ist sich dessen bewusst, und ich habe es auch in meiner Antwort erwähnt. Aber danke für den Kommentar.
Alix Axel

1
Zu Ihrer Information: Ich musste FILTER_FLAG_IPV6 entfernen, damit der Code von Alix Axel funktioniert.
darkAsPitch

2
@ rubenrp81 Der TCP-Socket-Handler ist die einzige kanonische Quelle, alles andere wird vom Angreifer gesteuert. Der obige Code ist der Traum eines Angreifers.
Turm

12

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.


Danke für die Eingabe. Ich verwende derzeit die IP-Adresse des Benutzers, um die Sitzungsauthentifizierung zu unterstützen, indem ich die IP-Adresse der Klasse C als einschränkenden Faktor zur Begrenzung der Sitzungsentführung verwende, aber dynamische IPs innerhalb eines angemessenen Rahmens zulasse. Gefälschte IPs und anonyme Proxyserver sind nur etwas, mit dem ich mich für eine ausgewählte Gruppe von Personen befassen muss.
Corey Ballou

@cballou - Sicherlich ist REMOTE_ADDR für diesen Zweck die richtige. Jeder Ansatz, der sich auf HTTP-Header stützt, ist anfällig für Header-Spoofing. Wie lange dauert eine Sitzung? Dynamische IPs ändern sich nicht schnell.
MZB

Sie tun dies, besonders wenn ich möchte, dass sie es tun (ändern Sie die Mac-Adresse, die viele Treiber unterstützen). Nur REMOTE_ADDR allein reicht aus, um den letzten Server zu erhalten, mit dem es gesprochen hat. In einer Proxy-Situation erhalten Sie also die Proxy-IP.

8

Wir gebrauchen:

/**
 * 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'];
    }
}

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.


Hoppla, ich habe gerade gemerkt, dass Sie im Grunde das Gleiche tun, wenn Sie explodieren und so weiter. Plus ein bisschen mehr. Ich bezweifle, dass meine Antwort viel Hilfe war. :)
Gabrielk

Dies gibt die Adresse des
lokalen

3

Meine Antwort ist im Grunde nur eine ausgefeilte, vollständig validierte und vollständig verpackte Version der Antwort von @ 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 = '';
    };

?>

Ä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.


2

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.


Schlagen Sie vor, unter Berücksichtigung der Tatsache, dass ich nach einer Lösung nur für PHP suche, $_SERVER['CLIENT_IP']als zweite if-Anweisung hinzuzufügen ?
Corey Ballou

Nein - nur wenn Sie den zurückgegebenen Daten eine Bedeutung beimessen möchten, ist es eine gute Idee, die Netzwerkendpunktadresse (Client-IP) sowie alles, was auf einen anderen Wert in Proxy-Headern hinweist, beizubehalten (z. B. möglicherweise) siehe viele 192.168.1.x-Adressen, aber von verschiedenen Client-IPs) C.
Symcbean

1

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.

// 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 = '';
}

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.

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

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 :)


1

Ich habe mir diese Funktion ausgedacht, die nicht einfach die IP-Adresse zurückgibt, sondern ein Array mit IP-Informationen.

// 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;
}

Hier ist die Funktion:

/**
 * 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;
}

1

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:

-- 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
...

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:

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";
   }

Nehmen wir zum Beispiel an, $this->token->get('attempts_before_ban') === 102 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 attemptsund ihn aus einer Kombination wie der folgenden erhalten können:

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

woher jwt_loadkommt 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 initiieren private $jwt.


0

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.


1
Nachdem ich die Wikipedia-Seite auf HTTP_X_FORWARDED_FOR gelesen habe: en.wikipedia.org/wiki/X-Forwarded-For ... Ich sehe, dass die vorgeschlagene Reihenfolge tatsächlich von links nach rechts ist, wie Ihr Code es hat. Aus unseren Protokollen geht jedoch hervor, dass es viele Fälle gibt, in denen dies von Proxys in freier Wildbahn nicht beachtet wird und die IP-Adresse, die Sie überprüfen möchten, an beiden Enden der Liste stehen kann.
Chris Withers

1
Oder in der Mitte, wie es passieren würde, wenn einige der Proxies die Reihenfolge von links nach rechts einhalten würden und andere nicht.
Brilliand

0

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 ():

    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;
    }

0

Hier ist eine modifizierte Version, wenn Sie CloudFlare- Caching-Layer-Dienste verwenden

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;
}

0

Nur eine VB.NET- Version der Antwort:

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

3
Es gibt Tag "PHP" in der Frage
Luchaninov

0

Nur ein weiterer sauberer Weg:

  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;
  }

0

Aus der Anforderungsklasse von 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);
}

Undefinierte Eigenschaft: $ server
C47

0

Ich bin überrascht, dass niemand filter_input erwähnt hat. Hier ist die Antwort von Alix Axel in einer Zeile zusammengefasst:

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);
}

-1

Sie haben so ziemlich Ihre eigene Frage beantwortet! :) :)

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;
}

Quelle


-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);
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.