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_ADDR
représente toujours la source la plus fiable d'une adresse IP. Les autres $_SERVER
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 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_ADDR
et ne vous embêtez pas à répondre à ceux qui se trouvent derrière un proxy.
la source
Réponses:
Voici un moyen plus court et plus propre d'obtenir l'adresse 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:Votre
HTTP_X_FORWARDED_FOR
extrait peut également être simplifié à partir de ceci:Pour ça:
Vous pouvez également vouloir valider les adresses IPv6.
la source
filter_var
correctif 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_FOR
optimisation est également très appréciée. Dans quelques minutes, je mettrai à jour le code.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.
la source
Nous utilisons:
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é.
la source
Ma réponse est fondamentalement juste une version polie, entièrement validée et entièrement packagée de la réponse de @ AlixAxel:
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.
la source
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.
la source
$_SERVER['CLIENT_IP']
comme deuxième instruction if?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ù.
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.
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 :)
la source
Je suis venu avec cette fonction qui ne renvoie pas simplement l'adresse IP mais un tableau avec des informations IP.
Voici la fonction:
la source
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:
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:
Disons, par exemple,
$this->token->get('attempts_before_ban') === 10
et 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
attempts
et que nous pouvons l'obtenir à partir d'une combinaison comme:d'où
jwt_load
vient 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 aprivate $jwt
.la source
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.
la source
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:
la source
Voici une version modifiée si vous utilisez les services de couche de mise en cache CloudFlare
la source
Juste une version VB.NET de la réponse:
la source
Juste une autre façon propre:
la source
De la classe Request de Symfony https://github.com/symfony/symfony/blob/1bd125ec4a01220878b3dbc3ec3156b073996af9/src/Symfony/Component/HttpFoundation/Request.php
la source
Je suis surpris que personne n'ait mentionné filter_input, alors voici la réponse d'Alix Axel condensée en une seule ligne:
la source
Vous avez à peu près répondu à votre propre question! :)
La source
la source
la source