web-dev-qa-db-fra.com

Quel est le moyen le plus précis de récupérer l'adresse IP correcte d'un utilisateur en PHP?

Je sais qu'il existe une pléthore d'en-têtes de variables $ _ SERVER disponibles pour la récupération d'adresse IP. Je me demandais s'il existait un consensus général sur la manière de récupérer de la manière la plus précise l'adresse IP réelle d'un utilisateur (sachant qu'aucune méthode n'est parfaite) à l'aide desdites variables?

J'ai passé un certain temps à essayer de trouver une solution en profondeur et j'ai créé le code suivant basé sur un certain nombre de sources. J'adorerais que quelqu'un puisse, s'il vous plaît, creuser des trous dans la réponse ou faire la lumière sur quelque chose peut-être plus précis.

l'édition inclut les optimisations de @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;
 }

Mots d'avertissement (mise à jour)

REMOTE_ADDR représente toujours la source la plus fiable d’une adresse IP. L'autre $_SERVER Les variables mentionnées ici peuvent être usurpées par un client distant très facilement. Le but de cette solution est de tenter de déterminer l'adresse IP d'un client assis derrière un proxy. Pour vos besoins généraux, vous pouvez envisager d’utiliser cette option en combinaison avec l’adresse IP renvoyée directement par $_SERVER['REMOTE_ADDR'] et stocker les deux.

Cette solution répondra parfaitement à vos besoins pour 99,9% des utilisateurs. Elle ne vous protégera pas des 0,1% d'utilisateurs malveillants qui cherchent à abuser de votre système en injectant leurs propres en-têtes de requête. Si vous vous fiez aux adresses IP pour des tâches critiques, utilisez REMOTE_ADDR et ne vous embêtez pas pour ceux qui se trouvent derrière un proxy.

275
Corey Ballou

Voici un moyen plus simple d’obtenir l’adresse 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;
                }
            }
        }
    }
}

J'espère que ça aide!


Votre code semble déjà assez complet, je ne vois pas de bogues possibles (hormis les mises en garde IP habituelles), je changerais la fonction validate_ip() pour qu'elle s'appuie sur l'extension de filtre:

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

De plus, votre extrait de code HTTP_X_FORWARDED_FOR Peut être simplifié à partir de ceci:

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

Pour ça:

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

Vous pouvez également vouloir valider les adresses IPv6.

234
Alix Axel

Même dans ce cas cependant, l'obtention de la véritable adresse IP d'un utilisateur sera peu fiable. Tout ce qu'ils ont à faire, c'est d'utiliser un serveur proxy anonyme (qui ne respecte pas les en-têtes de http_x_forwarded_for, http_forwarded, etc.) et vous obtenez uniquement l'adresse IP de leur serveur proxy.

Vous pouvez alors voir s’il existe une liste d’adresses IP de serveurs proxy anonymes, mais il n’ya aucun moyen de s’assurer qu’elles sont exactes à 100% également et le plus qu’il puisse faire est de vous faire savoir qu’il s’agit d’un serveur proxy. Et si quelqu'un est intelligent, il peut usurper les en-têtes pour les transferts HTTP.

Disons que je n'aime pas le collège local. Je découvre les adresses IP qu'ils ont enregistrées et je fais bannir leur adresse IP sur votre site en faisant de mauvaises choses, car je me rends compte que vous respectez les transferts HTTP. La liste est sans fin.

Ensuite, comme vous l'avez deviné, il existe des adresses IP internes telles que le réseau de collège que j'ai déjà mentionné. Beaucoup utilisent un format 10.x.x.x. Donc, tout ce que vous savez, c'est qu'il a été transféré pour un réseau partagé.

Ensuite, je ne commencerai pas beaucoup, mais les adresses IP dynamiques sont désormais la voie du haut débit. Alors. Même si vous obtenez une adresse IP d'utilisateur, attendez-vous à ce qu'elle change en 2 à 3 mois, au plus longtemps.

10
user192230

Nous utilisons:

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

L'explosion sur HTTP_X_FORWARDED_FOR est due à des problèmes étranges lors de la détection d'adresses IP lorsque Squid a été utilisé.

7
gabrielk

Ma réponse est fondamentalement une version polie, entièrement validée et entièrement intégrée de la réponse de @ 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 = '';
    };

?>

Changements:

  • Cela simplifie le nom de la fonction (avec le style de formatage 'camelCase').

  • Il inclut une vérification pour s’assurer que la fonction n’est pas déjà déclarée dans une autre partie de votre code.

  • Il prend en compte la compatibilité 'CloudFlare'.

  • Il initialise plusieurs noms de variables "liées à IP" à la valeur renvoyée, de la fonction 'getClientIP'.

  • Cela garantit que si la fonction ne renvoie pas une adresse IP valide, toutes les variables sont définies sur une chaîne vide au lieu de null.

  • C'est seulement (45) lignes de code.

4
James Anderson Jr.

La plus grande question est dans quel but?

Votre code est presque aussi complet qu'il pourrait l'être - mais je vois que si vous repérez ce qui ressemble à un en-tête ajouté au proxy, vous utilisez cet INSTEAD du CLIENT_IP, mais si vous souhaitez que ces informations soient utilisées à des fins d'audit, soyez averti - c'est très facile faire semblant.

Bien sûr, vous ne devriez jamais utiliser d’adresses IP pour aucune sorte d’authentification - même celles-ci peuvent être usurpées.

Vous pouvez obtenir une meilleure mesure de l'adresse IP du client en envoyant un flash ou Java applet qui se connecte au serveur via un port non http (qui révélerait donc des proxies transparents ou des cas où les en-têtes injectés par proxy sont faux - mais gardez à l'esprit que, lorsque le client peut SEULEMENT se connecter via un proxy Web ou que le port sortant est bloqué, il n'y aura pas de connexion à partir de l'applet.

C.

2
symcbean

je me rends compte qu'il existe des réponses bien meilleures et plus concises ci-dessus, et ce n'est pas une fonction, ni le script le plus gracieux qui soit. Dans notre cas, nous devions générer à la fois le spoofable x_forwarded_for et le plus fiable remote_addr dans un commutateur simpliste, par exemple. Il fallait permettre aux blancs d'injecter dans d'autres fonctions si-aucune ou si-singulier (plutôt que de simplement renvoyer la fonction préformatée). Il fallait une variable "on ou off" avec une ou plusieurs étiquettes personnalisées par commutateur pour les paramètres de la plate-forme. Il fallait également un moyen pour que $ ip soit dynamique en fonction de la demande afin de prendre la forme de forwarded_for.

Aussi, je n'ai vu personne adresser isset () vs! Empty () - il est possible de ne rien entrer pour x_forwarded_for tout en déclenchant la vérité isset (), ce qui entraîne une variable vierge, un moyen de se déplacer est d'utiliser && et de combiner les deux en tant que conditions. N'oubliez pas que vous pouvez usurper des mots tels que "PWNED" comme x_forwarded_for. Assurez-vous donc de stériliser à une syntaxe IP réelle si votre sortie est protégée ou stockée dans une base de données.

Vous pouvez également tester avec Google Translate si vous avez besoin d'un proxy multiple pour afficher le tableau dans x_forwarder_for. Si vous voulez usurper des en-têtes à tester, vérifiez cette extension En-tête client Chrome . Cela passera par défaut à remote_addr standard uniquement derrière un proxy.

Je ne connais pas les cas où remote_addr pourrait être vide, mais c'est là une solution de secours au cas où.

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

Pour rendre ces dynamiques dynamiques à utiliser dans une ou plusieurs fonctions ou dans une requête/écho/vue ci-dessous, par exemple, pour la génération de rapports de consignation ou d'erreurs, utilisez globals ou simplement des échos où vous le souhaitez sans créer une tonne d'autres conditions ou de sorties static-schema les fonctions.

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

Merci pour toutes vos bonnes pensées. S'il vous plaît laissez-moi savoir si cela pourrait être mieux, encore un peu nouveau à ces en-têtes :)

1
dhaupin

Comme quelqu'un l'a dit précédemment, la clé ici est la raison pour laquelle vous voulez stocker les ips de l'utilisateur.

Je vais donner un exemple tiré d’un système d’enregistrement sur lequel je travaille et bien sûr la solution, rien que pour contribuer à cette vieille discussion qui revient fréquemment dans mes recherches.

De nombreuses bibliothèques d'enregistrement php utilisent ip pour limiter/verrouiller les tentatives infructueuses basées sur l'ip de l'utilisateur. Considérez ce tableau:

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

Ensuite, lorsqu'un utilisateur essaie de se connecter ou de participer à un service tel qu'une réinitialisation de mot de passe, une fonction est appelée au début:

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

Disons, par exemple, $this->token->get('attempts_before_ban') === 10 et 2 utilisateurs viennent pour les mêmes ips que dans les codes précédents où les en-têtes peuvent être usurpés , puis après 5 tentatives chacune les deux sont interdites ! Pire encore, si tous proviennent du même proxy, seuls les 10 premiers utilisateurs seront connectés et tout le reste sera banni!

Le point crucial ici est qu’il nous faut un index unique sur la table attempts et nous pouvons l’obtenir à partir d’une combinaison telle que:

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

jwt_load provient d'un cookie http qui suit la technologie jeton Web Json où nous ne stockons que le crypté une charge qui devrait contenir une valeur arbitraire/unique pour chaque utilisateur. Bien sûr, la requête devrait être modifiée en: "SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ? AND jwt_load = ?" et la classe devrait également initier un private $jwt.

1
centurian

Je suis venu avec cette fonction qui ne renvoie pas simplement l'adresse IP mais un tableau avec des informations 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;
}

Voici la fonction:

/**
 * 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
Philipp

Juste un autre moyen propre:

  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
Liko

Voici une version modifiée si vous utilisez CloudFlare services de couche de mise en cache

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
jmserra

Merci pour cela, très utile.

Cela aiderait cependant si le code était syntaxiquement correct. Comme il y en a beaucoup trop autour de la ligne 20. Ce qui, j'en ai bien peur, signifie que personne ne l'a réellement essayé.

Je peux être fou, mais après l'avoir essayé sur quelques adresses valides et invalides, la seule version de validate_ip () qui a fonctionné était la suivante:

    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
Mark Boon

Juste une version VB.NET de la réponse:

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
0
Abacus

De la classe Request de Symfony https://github.com/symfony/symfony/blob/1bd125ec4a01220878b3dbc3ec6156b073996af9/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);
}
0
luchaninov

Je me demande si vous devriez peut-être parcourir le HTTP_X_FORWARDED_FOR éclaté dans l’ordre inverse, car j’ai appris que l’adresse IP de l’utilisateur se retrouvait à la fin de la liste séparée par des virgules; plus susceptibles d’obtenir l’adresse IP de l’un des mandataires renvoyés, ce qui pourrait éventuellement permettre le détournement de session car de nombreux utilisateurs peuvent passer par ce proxy.

0
Chris Withers