Quelle est la manière la plus précise de récupérer l'adresse IP correcte d'un utilisateur en PHP?

301

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 y avait un consensus général sur la façon de récupérer le plus précisément la véritable adresse IP d'un utilisateur (sachant bien qu'aucune méthode n'est parfaite) en utilisant lesdites variables?

J'ai passé un certain temps à essayer de trouver une solution approfondie et j'ai trouvé le code suivant basé sur un certain nombre de sources. Je serais ravi que quelqu'un puisse faire des trous dans la réponse ou éclaircir quelque chose de plus précis.

la modification inclut des 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_ADDRreprésente toujours la source la plus fiable d'une adresse IP. Les autres $_SERVERvariables 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 de l'utiliser en combinaison avec l'adresse IP renvoyée directement par les deux $_SERVER['REMOTE_ADDR']et de les stocker.

Pour 99,9% des utilisateurs, cette solution répondra parfaitement à vos besoins. Il ne vous protégera pas du 0,1% d'utilisateurs malveillants qui cherchent à abuser de votre système en injectant leurs propres en-têtes de demande. Si vous comptez sur les adresses IP pour quelque chose de mission critique, recourez à REMOTE_ADDRet ne vous embêtez pas à répondre à ceux qui se trouvent derrière un proxy.

Corey Ballou
la source
2
Pour la question whatismyip.com, je pense qu'ils font quelque chose comme ce script, l'exécutez-vous localement? Si c'est la raison pour laquelle vous avez une IP interne, rien n'est envoyé via l'interface publique dans ce cas, donc il n'y a aucune information à obtenir pour php
Matt
2
Assurez-vous de garder cela à l'esprit lors de la mise en œuvre de ceci: stackoverflow.com/questions/1672827/…
Kevin Peno
19
N'oubliez pas que tous ces en-têtes HTTP sont vraiment faciles à modifier: avec votre solution, il me suffit de configurer mon navigateur pour envoyer un en-tête X-Forwarded-For avec une IP aléatoire et votre script retournera avec plaisir une fausse adresse. Donc, selon ce que vous essayez de faire, cette solution pourrait être moins fiable que d'utiliser simplement REMOTE_ADDR.
gnomnain
14
OMFG, "ip peu fiable"! La première fois que je vois un tel non-sens ici sur SO. La seule adresse IP fiable est REMOTE_ADDR
Your Common Sense
3
-1 ceci est vulnérable à l'usurpation d'identité. Tout ce que vous faites, c'est demander à l'utilisateur quelle devrait être son adresse IP.
tour le

Réponses:

269

Voici un moyen plus court et plus propre 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 aucun bogue possible (à part les mises en garde IP habituelles), je changerais la validate_ip()fonction pour compter sur l'extension du 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;
}

Votre HTTP_X_FORWARDED_FORextrait peut également ê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.

Alix Axel
la source
4
J'apprécie vraiment le filter_varcorrectif car il supprime un tas de contrôles int entiers non signés sur l'adresse IP. J'aime aussi le fait que cela me donne également la possibilité de valider les adresses IPv6. L' HTTP_X_FORWARDED_FORoptimisation est également très appréciée. Dans quelques minutes, je mettrai à jour le code.
Corey Ballou
33
-1 cela est vulnérable à l'usurpation d'identité, tout ce que vous faites est de demander à l'utilisateur quelle devrait être son adresse IP.
tour le
7
@Rook: Oui, je sais. Le PO en est conscient et je l'ai également mentionné dans ma réponse. Mais merci pour le commentaire.
Alix Axel
1
FYI: J'ai dû supprimer FILTER_FLAG_IPV6 pour que le code d'Alix Axel fonctionne.
darkAsPitch
2
@ rubenrp81 Le gestionnaire de socket TCP est la seule source canonique, tout le reste est contrôlé par l'attaquant. Le code ci-dessus est le rêve d'un attaquant.
tour
12

Cependant, même dans ce cas, obtenir la véritable adresse IP d'un utilisateur ne sera pas fiable. Tout ce qu'ils doivent faire est d'utiliser un serveur proxy anonyme (qui n'honore pas les en-têtes pour http_x_forwarded_for, http_forwarded, etc.) et tout ce que vous obtenez est l'adresse IP de leur serveur proxy.

Vous pouvez alors voir s'il existe une liste d'adresses IP de serveur proxy qui sont anonymes, mais il n'y a aucun moyen de s'assurer qu'elle est également exacte à 100% et le plus qu'elle ferait 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 HTTP.

Disons que je n'aime pas le collège local. Je détermine quelles adresses IP ils ont enregistrées et je fais interdire leur adresse IP sur votre site en faisant de mauvaises choses, parce que je pense que vous respectez les transferts HTTP. La liste est interminable.

Ensuite, il y a, comme vous l'avez deviné, des adresses IP internes telles que le réseau universitaire que j'ai mentionné auparavant. Beaucoup utilisent un format 10.xxx. Donc, tout ce que vous savez, c'est qu'il a été transféré pour un réseau partagé.

Ensuite, je ne m'y attarderai 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 dans 2 à 3 mois, au plus long.

Peter Mortensen
la source
Merci pour la contribution. J'utilise actuellement l'adresse IP de l'utilisateur pour faciliter l'authentification de session en utilisant son IP de classe C comme facteur limitant pour limiter le détournement de session mais permettre des IP dynamiques dans des limites raisonnables. Les adresses IP usurpées et les serveurs proxy anonymes sont juste quelque chose que je devrai gérer pour un groupe sélectionné de personnes.
Corey Ballou
@cballou - À cette fin, REMOTE_ADDR est certainement la bonne à utiliser. Toute approche reposant sur des en-têtes HTTP est vulnérable à l'usurpation d'en-tête. Combien de temps dure une session? Les adresses IP dynamiques ne changent pas rapidement.
MZB
Ils le font, surtout si je le veux (changer l'adresse mac que de nombreux pilotes prennent en charge). À lui seul, REMOTE_ADDR suffit pour obtenir le dernier serveur auquel il a parlé. Ainsi, dans une situation de proxy, vous obtenez l'IP proxy.
8

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 avons dû détecter des adresses IP lorsque Squid a été utilisé.

Gabrielk
la source
Oups, je viens de réaliser que vous faites essentiellement la même chose avec l'explosion, et ainsi de suite. Plus un petit plus. Je doute donc que ma réponse ait été d'une grande aide. :)
gabrielk
Cela renvoie l'adresse du localhost
Scarl
3

Ma réponse est fondamentalement juste une version polie, entièrement validée et entièrement packagé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:

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

  • Il comprend une vérification pour vous 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és à IP" à la valeur retournée, de la fonction 'getClientIP'.

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

  • Ce ne sont que (45) lignes de code.

James Anderson Jr.
la source
2

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é par proxy, vous utilisez cette INSTEAD du CLIENT_IP, cependant si vous voulez que ces informations à des fins d'audit soient prévenues - c'est très facile truquer.

Certes, vous ne devez jamais utiliser d'adresses IP pour toute sorte d'authentification - même celles-ci peuvent être usurpées.

Vous pouvez obtenir une meilleure mesure de l'adresse IP du client en poussant une applet flash ou java qui se connecte au serveur via un port non http (ce qui révélerait donc des mandataires 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 depuis l'applet.

C.

symcbean
la source
Compte tenu du fait que je recherche une solution PHP uniquement, proposez-vous que j'ajoute $_SERVER['CLIENT_IP']comme deuxième instruction if?
Corey Ballou
Non - juste que si vous souhaitez accorder une importance aux données retournées, ce serait une bonne idée de conserver l'adresse du point de terminaison réseau (IP client) ainsi que tout ce qui suggère une valeur différente dans les en-têtes ajoutés par proxy (par exemple, vous pouvez voir beaucoup d'adresses 192.168.1.x mais provenant de différents clients ips) C.
symcbean
1

Je me rends compte qu'il y a 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 produire à la fois le x_forwarded_for spoofable et le remote_addr plus fiable dans un commutateur simpliste par exemple. Il fallait autoriser les blancs à injecter dans d'autres fonctions if-none ou if-singular (plutôt que de simplement renvoyer la fonction préformatée). Il avait besoin d'une var "on ou off" avec une étiquette personnalisée 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 qu'il prenne la forme de forwarded_for.

De plus, je n'ai vu personne adresser isset () vs! Empty () - il est possible de ne rien saisir pour x_forwarded_for tout en déclenchant toujours la vérité isset () résultant en var vide, un moyen de contourner le problème est d'utiliser && et de combiner les deux comme conditions. Gardez à l'esprit que vous pouvez usurper des mots comme "PWNED" en tant que x_forwarded_for alors assurez-vous de stériliser à une véritable syntaxe IP si votre sortie est quelque part protégée ou dans DB.

Vous pouvez également tester à l'aide de google translate si vous avez besoin d'un multi-proxy pour voir le tableau dans x_forwarder_for. Si vous souhaitez tester les en-têtes d'usurpation, consultez cette extension Chrome Client Header Spoof . Ce sera par défaut à juste remote_addr standard tout en étant derrière un proxy.

Je ne connais aucun cas où remote_addr pourrait être vide, mais c'est là comme solution de rechange 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 à utiliser dans la ou les fonctions ou les requêtes / échos / vues ci-dessous, par exemple pour la génération de journaux ou les rapports d'erreurs, utilisez des globaux ou faites-les simplement écho là où vous le souhaitez sans faire une tonne d'autres conditions ou sortie de schéma statique 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. Veuillez me faire savoir si cela pourrait être mieux, encore un peu nouveau pour ces en-têtes :)

dhaupin
la source
1

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;
}
Philipp
la source
1

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

Je vais vous donner un exemple d'un système d'enregistrement sur lequel je travaille et bien sûr la solution juste pour contribuer qc dans 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 tout ce qui est lié à la maintenance comme 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') === 10et 2 utilisateurs viennent pour les mêmes ips comme c'est le cas dans les codes précédents où les en-têtes peuvent être usurpés , puis après 5 tentatives, les deux sont interdits ! Pire encore, si tous proviennent du même proxy, seuls les 10 premiers utilisateurs seront enregistrés et tous les autres seront interdits!

La critique ici est que nous avons besoin d'un index unique sur la table attemptset que nous pouvons l'obtenir à partir d'une combinaison comme:

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

d'où jwt_loadvient un cookie http qui suit la technologie de jeton Web json où nous stockons uniquement la charge utile chiffrée qui devrait contenir une valeur arbitraire / unique pour chaque utilisateur. Bien sûr, la demande doit être modifiée pour: "SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ? AND jwt_load = ?"et la classe doit également initier a private $jwt.

centurien
la source
0

Je me demande si vous devriez peut-être parcourir le HTTP_X_FORWARDED_FOR éclaté dans l'ordre inverse, car mon expérience a été que l'adresse IP de l'utilisateur finit à la fin de la liste séparée par des virgules, donc en commençant au début de l'en-tête, vous êtes plus susceptibles d'obtenir l'adresse IP de l'un des proxys retournés, ce qui pourrait encore permettre le détournement de session car de nombreux utilisateurs peuvent passer par ce proxy.

Chris Withers
la source
1
Après avoir lu la page wikipedia sur HTTP_X_FORWARDED_FOR: en.wikipedia.org/wiki/X-Forwarded-For ... Je vois que l'ordre suggéré est, en effet, de gauche à droite car votre code l'a. Cependant, à partir de nos journaux, je peux voir qu'il y a beaucoup de cas où cela n'est pas respecté par les procurations dans la nature et l'adresse IP que vous souhaitez vérifier pourrait être à l'une ou l'autre extrémité de la liste.
Chris Withers
1
Ou au milieu, comme ce serait le cas si certains mandataires respectaient l'ordre de gauche à droite et d'autres non.
Brilliand
0

Merci pour cela, très utile.

Cela aiderait cependant si le code était syntaxiquement correct. En l'état, il y en a {trop autour de la ligne 20. Ce qui, je le crains, signifie que personne n'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;
    }
Mark Boon
la source
0

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

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;
}
jmserra
la source
0

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
Abaque
la source
3
Il y a la balise "PHP" dans la question
luchaninov
0

Juste une autre façon 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;
  }
Liko
la source
0

De la classe Request de 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);
}
luchaninov
la source
Propriété non définie: $ server
C47
0

Je suis surpris que personne n'ait mentionné filter_input, alors voici la réponse d'Alix Axel condensée en une seule ligne:

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);
}
mattavatar
la source
-1

Vous avez à peu près répondu à votre propre question! :)

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

La source

Alex Weber
la source
-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);
}
Meketrefe
la source