325 votes

Ce qui est la manière la plus précise pour récupérer un utilisateur ' adresse IP correcte de s en PHP ?

Je sais qu'il y a une pléthore de $_SERVER variables en-têtes disponibles pour l'extraction de l'adresse IP. Je me demandais si il y a un consensus général quant à la façon la plus précise de récupérer un utilisateur de l'adresse IP réelle (sachant bien aucune méthode n'est parfaite) utilisation de variables?

J'ai passé du temps à essayer de trouver un remaniement en profondeur de la solution et est venu avec le code suivant, basé sur un certain nombre de sources. J'aimerais si quelqu'un pourrait s'il vous plaît faire des trous dans la réponse ou de jeter un peu de lumière sur quelque chose peut-être plus exact.

edit comprend des améliorations 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;
 }

Paroles d'Avertissement (mise à jour)

REMOTE_ADDR représente encore la plus fiable source d'une adresse IP. Les autres $_SERVER variables mentionnées ici peuvent être usurpée par un client à distance 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 fins générales, vous pouvez envisager d'utiliser cette combinaison avec l'adresse IP renvoyée directement à partir de $_SERVER['REMOTE_ADDR'] et de stocker à la fois.

Pour 99,9% des utilisateurs de cette solution permettra de répondre à vos besoins parfaitement. Il ne sera pas vous protéger contre les 0,1% des utilisateurs malveillants qui cherchent à abuser de votre système en injectant leurs propres en-têtes de requête. Si en s'appuyant sur les adresses IP pour quelque chose d'essentiel à la mission, le recours à des REMOTE_ADDR et ne pas la peine de restauration pour ceux qui sont derrière un proxy.

301voto

Alix Axel Points 63455

Voici un court, le moyen le plus propre pour 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 cela aide!


Votre code semble être assez complet déjà, je ne vois pas tous les possibles bugs (à côté de l'adresse IP habituelle mises en garde), je voudrais changer l' validate_ip() fonction à compter 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;
}

Aussi votre HTTP_X_FORWARDED_FOR extrait peut être simplifiée à 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 cela:

// 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 valider des adresses IPv6.

9voto

gabrielk Points 432

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 que nous avions détectés lors de l'utilisation de Squid .

3voto

symcbean Points 27412

La grande question est dans quel but?

Votre code est presque aussi complet qu'il peut l'être - mais je vois que si vous repérez ce qui ressemble à un proxy ajouté en-tête, vous l'utiliser à la PLACE de la CLIENT_IP, cependant, si vous voulez cette information à des fins de vérification alors averti (e) - il est très facile à faux.

Certes, vous ne devez jamais utiliser les adresses IP pour toute sorte d'authentification - même ceux-ci peuvent être falsifiés.

Vous pourriez obtenir une meilleure mesure de l'adresse ip du client en poussant un flash ou une applet java qui se connecte au serveur par l'intermédiaire d'un non-port http (qui serait donc révéler transparent proxy ou de cas où le proxy-injecté en-têtes sont faux, mais garder à l'esprit que, lorsque le client ne peut se connecter via un proxy web ou le port sortant est bloqué, il n'y aura pas de connexion à partir de l'applet.

C.

0voto

Abacus Points 499

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
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X