web-dev-qa-db-fra.com

Comment utiliser correctement HTTP_X_FORWARDED_FOR?

D'accord, j'ai un petit problème d'authentification. Mon service Web permet de se connecter à mon API via HTTP avec un nom d'utilisateur et un mot de passe, mais cette connexion peut également être limitée à une adresse IP spécifique.

Cela signifie que le $_SERVER['REMOTE_ADDR'] peut être incorrect. Je sais déjà que toute information IP ne peut jamais vraiment être invoquée - j'ai la restriction uniquement dans le but d'ajouter une autre couche de sécurité.

S'il s'agit de l'aperçu général d'une demande adressée à mon serveur Web:

clientSERVER => clientPROXY => myPROXY => mySERVER

Cela signifie que mySERVER affiche REMOTE_ADDR de myPROXY au lieu de celle du client et envoie l'IP réelle du client en tant que HTTP_X_FORWARDED_FOR.

Pour surmonter cela, mon service Web a une liste d'adresses IP de "proxy de confiance" et si REMOTE_ADDR provient d'une de ces adresses IP de confiance, puis il indique à mon service Web que l'adresse IP réelle est la valeur de HTTP_X_FORWARDED_FOR.

Maintenant, le problème est avec clientPROXY. Cela signifie que (assez souvent) mySERVER obtient HTTP_X_FORWARDED_FOR valeur qui a plusieurs adresses IP. Selon HTTP_X_FORWARDED_FOR documentation, la valeur est une liste d'adresses IP séparées par des virgules où la première IP est celle du vrai client réel et toutes les autres adresses IP sont celles d'un proxy.

Donc si HTTP_X_FORWARDED_FOR a plusieurs valeurs et mon service est limité par IP, dois-je vérifier la "dernière" valeur de HTTP_X_FORWARDED_FOR par rapport à ma liste d'adresses IP autorisées et ignorer simplement l'IP client réel?

Je suppose que dans un système, où je dois définir la liste des adresses IP autorisées, l'adresse IP sur liste blanche devrait être celle d'un proxy et non une IP qui se trouve derrière le proxy (car cela pourrait être une adresse IP locale et changer fréquemment) .

Et que dire de HTTP_CLIENT_IP?

34
kingmaple

Vous pouvez utiliser cette fonction pour obtenir l'IP client appropriée:

public function getClientIP(){       
     if (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)){
            return  $_SERVER["HTTP_X_FORWARDED_FOR"];  
     }else if (array_key_exists('REMOTE_ADDR', $_SERVER)) { 
            return $_SERVER["REMOTE_ADDR"]; 
     }else if (array_key_exists('HTTP_CLIENT_IP', $_SERVER)) {
            return $_SERVER["HTTP_CLIENT_IP"]; 
     } 

     return '';
}
25

J'aime la réponse de Hrishikesh, à laquelle je n'ai que ceci à ajouter ... parce que nous avons vu une chaîne délimitée par des virgules traverser lorsque plusieurs proxys en cours de route ont été utilisés, nous avons jugé nécessaire d'ajouter une explosion et de saisir la valeur finale, comme cette:

$IParray=array_values(array_filter(explode(',',$_SERVER['HTTP_X_FORWARDED_FOR'])));
return end($IParray);

le array_filter est là pour supprimer les entrées vides.

21
user336828

À la lumière des dernières vulnérabilités httpoxy , il y a vraiment un besoin d'un exemple complet, comment utiliser HTTP_X_FORWARDED_FOR correctement.

Voici donc un exemple écrit en PHP, comment détecter une adresse IP de client, si vous savez que le client peut être derrière un proxy et que vous savez que ce proxy peut être approuvé. Si vous ne connaissez aucun proxy de confiance, utilisez simplement REMOTE_ADDR

<?php

function get_client_ip ()
{
    // Nothing to do without any reliable information
    if (!isset ($_SERVER['REMOTE_ADDR'])) {
        return NULL;
    }

    // Header that is used by the trusted proxy to refer to
    // the original IP
    $proxy_header = "HTTP_X_FORWARDED_FOR";

    // List of all the proxies that are known to handle 'proxy_header'
    // in known, safe manner
    $trusted_proxies = array ("2001:db8::1", "192.168.50.1");

    if (in_array ($_SERVER['REMOTE_ADDR'], $trusted_proxies)) {

        // Get the IP address of the client behind trusted proxy
        if (array_key_exists ($proxy_header, $_SERVER)) {

            // Header can contain multiple IP-s of proxies that are passed through.
            // Only the IP added by the last proxy (last IP in the list) can be trusted.
            $proxy_list = explode (",", $_SERVER[$proxy_header]);
            $client_ip = trim (end ($proxy_list));

            // Validate just in case
            if (filter_var ($client_ip, FILTER_VALIDATE_IP)) {
                return $client_ip;
            } else {
                // Validation failed - beat the guy who configured the proxy or
                // the guy who created the trusted proxy list?
                // TODO: some error handling to notify about the need of punishment
            }
        }
    }

    // In all other cases, REMOTE_ADDR is the ONLY IP we can trust.
    return $_SERVER['REMOTE_ADDR'];
}

print get_client_ip ();

?>
13
Erki A

Vous pouvez également résoudre ce problème via la configuration Apache en utilisant mod_remoteip , en ajoutant ce qui suit à un fichier conf.d:

RemoteIPHeader X-Forwarded-For
RemoteIPInternalProxy 172.16.0.0/12
LogFormat "%a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
2
zeroimpl

Si vous l'utilisez dans une base de données, c'est un bon moyen:

Définissez le champ ip dans la base de données sur varchar (250), puis utilisez ceci:

$theip = $_SERVER["REMOTE_ADDR"];

if (!empty($_SERVER["HTTP_X_FORWARDED_FOR"])) {
    $theip .= '('.$_SERVER["HTTP_X_FORWARDED_FOR"].')';
}

if (!empty($_SERVER["HTTP_CLIENT_IP"])) {
    $theip .= '('.$_SERVER["HTTP_CLIENT_IP"].')';
}

$realip = substr($theip, 0, 250);

Ensuite, vous venez de vérifier $ realip par rapport au champ ip de la base de données

0
mowgli