Qual è il modo più preciso per recuperare l'indirizzo IP corretto di un utente in PHP?


301

So che ci sono moltissime intestazioni di variabili $ _SERVER disponibili per il recupero dell'indirizzo IP. Mi chiedevo se esiste un consenso generale su come recuperare in modo più accurato l'indirizzo IP reale di un utente (ben sapendo che nessun metodo è perfetto) utilizzando tali variabili?

Ho trascorso un po 'di tempo a cercare una soluzione approfondita e ho trovato il seguente codice basato su una serie di fonti. Mi piacerebbe se qualcuno potesse per favore fare buchi nella risposta o fare luce su qualcosa forse più preciso.

modifica include ottimizzazioni da @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;
 }

Words of Warning (aggiornamento)

REMOTE_ADDRrappresenta ancora la fonte più affidabile di un indirizzo IP. Le altre $_SERVERvariabili menzionate qui possono essere facilmente falsificate da un client remoto. Lo scopo di questa soluzione è tentare di determinare l'indirizzo IP di un client seduto dietro un proxy. Per i tuoi scopi generali, potresti considerare di usarlo in combinazione con l'indirizzo IP restituito direttamente da $_SERVER['REMOTE_ADDR']e memorizzandoli entrambi.

Per il 99,9% degli utenti questa soluzione soddisferà perfettamente le tue esigenze. Non ti proteggerà dallo 0,1% degli utenti malintenzionati che cercano di abusare del tuo sistema iniettando le proprie intestazioni di richiesta. Se si fa affidamento su indirizzi IP per qualcosa di fondamentale, ricorrere REMOTE_ADDRe non preoccuparsi di provvedere a coloro che stanno dietro un proxy.


2
Per la domanda whatismyip.com, sto pensando che facciano qualcosa come questo script, lo stai eseguendo localmente? Se sei il motivo per cui hai un IP interno, nulla viene inviato tramite l'interfaccia pubblica in quel caso, quindi non ci sono informazioni per php da ottenere
Matt

2
Assicurati di tenerlo presente durante l'implementazione: stackoverflow.com/questions/1672827/…
Kevin Peno

19
Ricorda che tutte queste intestazioni HTTP sono davvero facili da modificare: con la tua soluzione, devo solo configurare il mio browser per inviare un'intestazione X-Forwarded-For con un IP casuale e il tuo script restituirà felicemente un indirizzo falso. Quindi, a seconda di ciò che stai cercando di fare, questa soluzione potrebbe essere meno affidabile del semplice utilizzo di REMOTE_ADDR.
Gnomnain,

14
OMFG, "ip inaffidabile"! La prima volta che vedo una tale assurdità qui su SO. L'unico indirizzo IP affidabile è REMOTE_ADDR
Il tuo senso comune

3
-1 questo è vulnerabile allo spoofing. Tutto quello che stai facendo è chiedere all'utente quale dovrebbe essere il suo indirizzo IP.
arrosto

Risposte:


269

Ecco un modo più breve e più pulito per ottenere l'indirizzo 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;
                }
            }
        }
    }
}

Spero possa essere d'aiuto!


Il tuo codice sembra essere già abbastanza completo, non riesco a vedere alcun bug possibile (a parte i soliti avvertimenti IP), cambierei la validate_ip()funzione per fare affidamento sull'estensione del filtro però:

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

Anche il tuo HTTP_X_FORWARDED_FORframmento può essere semplificato da questo:

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

A questa:

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

Potresti anche voler convalidare gli indirizzi IPv6.


4
Apprezzo sicuramente la filter_varcorrezione in quanto rimuove un sacco di controlli int non firmati hacker sull'indirizzo IP. Mi piace anche il fatto che mi dia la possibilità di convalidare anche gli indirizzi IPv6. Anche l' HTTP_X_FORWARDED_FORottimizzazione è molto apprezzata. Tra qualche minuto aggiornerò il codice.
Corey Ballou,

33
-1 questo è vulnerabile allo spoofing. Tutto ciò che stai facendo è chiedere all'utente quale dovrebbe essere il suo indirizzo IP.
arrosto il

7
@Rook: Sì, lo so. L'OP ne è consapevole e l'ho anche menzionato nella mia risposta. Ma grazie per il commento.
Alix Axel,

1
Cordiali saluti: ho dovuto rimuovere FILTER_FLAG_IPV6 per far funzionare il codice di Alix Axel.
darkAsPitch

2
@ rubenrp81 Il gestore di socket TCP è l'unica fonte canonica, tutto il resto è controllato dagli aggressori. Il codice sopra è il sogno di un attaccante.
torre

12

Anche in questo caso, tuttavia, ottenere l'indirizzo IP reale di un utente non sarà affidabile. Tutto quello che devono fare è utilizzare un server proxy anonimo (uno che non rispetta le intestazioni di http_x_forwarded_for, http_forwarded, ecc.) E tutto ciò che ottieni è l'indirizzo IP del loro server proxy.

Puoi quindi vedere se esiste un elenco di indirizzi IP del server proxy che sono anonimi, ma non c'è modo di essere sicuri che sia accurato al 100% e che il massimo che farebbe è farti sapere che si tratta di un server proxy. E se qualcuno è intelligente, può falsificare le intestazioni per i forward HTTP.

Diciamo che non mi piace il college locale. Capisco quali indirizzi IP hanno registrato e ottenere il loro indirizzo IP vietato sul tuo sito facendo cose cattive, perché ho capito che onori l'HTTP in avanti. L'elenco è infinito.

Poi, come hai indovinato, ci sono indirizzi IP interni come la rete universitaria che ho incontrato prima. Molto usano un formato 10.xxx. Quindi tutto ciò che sapresti è che è stato inoltrato per una rete condivisa.

Quindi non inizierò molto in esso, ma gli indirizzi IP dinamici sono più la via della banda larga. Così. Anche se ottieni un indirizzo IP utente, aspettati che cambi tra 2 - 3 mesi, al massimo.


Grazie per l'input. Attualmente sto utilizzando l'indirizzo IP dell'utente per facilitare l'autenticazione della sessione utilizzando il loro IP di classe C come fattore limitante per limitare il dirottamento della sessione ma consentire IP dinamici entro limiti ragionevoli. Gli IP falsificati e i server proxy anonimi sono solo qualcosa che dovrò affrontare per un gruppo selezionato di persone.
Corey Ballou,

@cballou - Sicuramente per questo scopo REMOTE_ADDR è quello corretto da usare. Qualsiasi approccio basato sulle intestazioni HTTP è vulnerabile allo spoofing delle intestazioni. Quanto dura una sessione? Gli IP dinamici non cambiano rapidamente.
MZB,

Lo fanno, specialmente se lo voglio (cambiare l'indirizzo mac che molti driver supportano). Basta REMOTE_ADDR da solo per ottenere qualunque sia l'ultimo server con cui ha parlato. Quindi in una situazione proxy ottieni l'IP proxy.

8

Noi usiamo:

/**
 * 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'esplosione su HTTP_X_FORWARDED_FOR è dovuta a strani problemi che abbiamo riscontrato nel rilevamento di indirizzi IP quando è stato utilizzato Squid .


Spiacenti, ho appena realizzato che fai praticamente la stessa cosa esplodendo e così via. Inoltre un piccolo extra. Quindi dubito che la mia risposta sia stata di grande aiuto. :)
gabrielk,

Questo restituisce l'indirizzo del localhost
Scarl

3

La mia risposta è fondamentalmente solo una versione raffinata, completamente convalidata e completamente confezionata, della risposta di @ 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 = '';
    };

?>

I cambiamenti:

  • Semplifica il nome della funzione (con stile di formattazione 'camelCase').

  • Include un controllo per assicurarsi che la funzione non sia già dichiarata in un'altra parte del codice.

  • Tiene conto della compatibilità 'CloudFlare'.

  • Inizializza più nomi di variabili "relativi all'IP" sul valore restituito, della funzione 'getClientIP'.

  • Assicura che se la funzione non restituisce un indirizzo IP valido, tutte le variabili sono impostate su una stringa vuota, anziché null.

  • Sono solo (45) righe di codice.


2

La domanda più grande è a quale scopo?

Il tuo codice è quasi completo quanto potrebbe essere - ma vedo che se vedi ciò che sembra un'intestazione aggiunta proxy, usi INSTEAD di CLIENT_IP, tuttavia se vuoi che queste informazioni a scopo di controllo siano avvisate - è molto facile fingere.

Certamente non dovresti mai usare gli indirizzi IP per nessun tipo di autenticazione - anche questi possono essere falsificati.

È possibile ottenere una migliore misurazione dell'indirizzo IP del client inviando un applet flash o java che si ricollega al server tramite una porta non http (che rivelerebbe quindi proxy trasparenti o casi in cui le intestazioni iniettate dal proxy sono false - ma tenere presente che, laddove il client può connettersi SOLO tramite un proxy Web o la porta in uscita è bloccata, non ci sarà alcuna connessione dall'applet.

C.


Tenendo conto che sto cercando una soluzione unica per PHP, stai suggerendo di aggiungere $_SERVER['CLIENT_IP']la seconda istruzione if?
Corey Ballou,

No, solo che se si desidera attribuire un significato ai dati restituiti, sarebbe una buona idea preservare l'indirizzo dell'endpoint di rete (IP client) e qualsiasi cosa che suggerisca un valore diverso nelle intestazioni aggiunte proxy (ad es. vedere molti indirizzi 192.168.1.x ma provenienti da diversi client ips) C.
symcbean

1

mi rendo conto che ci sono risposte molto migliori e più concise sopra, e questa non è una funzione né lo script più grazioso in circolazione. Nel nostro caso dovevamo produrre sia lo spoofable x_forwarded_for che il più affidabile remote_addr in uno switch semplicistico. Doveva consentire spazi vuoti per l'iniezione in altre funzioni if-none o if-singular (piuttosto che restituire semplicemente la funzione preformattata). Aveva bisogno di un var "on o off" con un'etichetta personalizzata per switch per le impostazioni della piattaforma. Aveva anche bisogno di un modo per $ ip di essere dinamico a seconda della richiesta in modo che prendesse la forma di forwarded_for.

Inoltre, non ho visto nessuno indirizzare isset () vs! Empty () - è possibile non inserire nulla per x_forwarded_for eppure innescare ancora verità isset () con conseguente var vuoto, un modo per aggirare è usare && e combinare entrambi come condizioni. Tieni presente che puoi falsificare parole come "PWNED" come x_forwarded_for, quindi assicurati di sterilizzare in una vera sintassi IP se esegui l'output da qualche parte protetto o nel DB.

Inoltre, puoi testare utilizzando google translate se hai bisogno di un multi-proxy per vedere l'array in x_forwarder_for. Se desideri testare le intestazioni di spoofing, dai un'occhiata all'estensione di spoofing delle intestazioni del client Chrome . L'impostazione predefinita è solo remote_addr mentre è dietro un proxy anon.

Non so in nessun caso in cui remote_addr potrebbe essere vuoto, ma è lì come fallback per ogni evenienza.

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

Per renderli dinamici per l'uso nelle funzioni o query / eco / viste di seguito, dì per la generazione di log o la segnalazione degli errori, usa i globi o fai eco ovunque tu voglia senza creare tonnellate di altre condizioni o output dello schema statico funzioni.

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

Grazie per tutti i tuoi grandi pensieri. Per favore fatemi sapere se questo potrebbe essere migliore, ancora un po 'nuovo per queste intestazioni :)


1

Mi è venuta in mente questa funzione che non restituisce semplicemente l'indirizzo IP ma un array con informazioni 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;
}

Ecco la funzione:

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

Come qualcuno ha detto in precedenza, la chiave qui è per quale motivo si desidera memorizzare ips dell'utente.

Fornirò un esempio da un sistema di registrazione su cui lavoro e, naturalmente, la soluzione solo per contribuire con questa vecchia discussione che viene frequentemente nelle mie ricerche.

Molte librerie di registrazione php usano ip per limitare / bloccare i tentativi falliti basati sull'ip dell'utente. Considera questa tabella:

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

Quindi, quando un utente tenta di eseguire un accesso o qualsiasi cosa relativa alla manutenzione come un ripristino della password, una funzione viene chiamata all'avvio:

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

Supponiamo, ad esempio, $this->token->get('attempts_before_ban') === 10che 2 utenti arrivino per gli stessi ips del caso dei codici precedenti in cui le intestazioni possono essere falsificate , quindi dopo 5 tentativi vengono banditi entrambi ! Ancora peggio, se tutti provengono dallo stesso proxy, verranno registrati solo i primi 10 utenti e tutto il resto verrà bandito!

La cosa fondamentale qui è che abbiamo bisogno di un indice univoco sulla tabella attemptse possiamo ottenerlo da una combinazione come:

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

da dove jwt_loadproviene un cookie http che segue la tecnologia token json web in cui archiviamo solo il payload crittografato che dovrebbe contenere un valore arbitrario / univoco per ogni utente. Naturalmente la richiesta dovrebbe essere modificata in: "SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ? AND jwt_load = ?"e la classe dovrebbe anche iniziare a private $jwt.


0

Mi chiedo se forse dovresti iterare su HTTP_X_FORWARDED_FOR esploso in ordine inverso, poiché la mia esperienza è stata che l'indirizzo IP dell'utente termina alla fine dell'elenco separato da virgole, quindi a partire dall'inizio dell'intestazione, sei è più probabile che venga restituito l'indirizzo IP di uno dei proxy, il che potrebbe potenzialmente consentire il dirottamento della sessione poiché molti utenti potrebbero passare attraverso quel proxy.


1
Dopo aver letto la pagina di Wikipedia su HTTP_X_FORWARDED_FOR: en.wikipedia.org/wiki/X-Fwardward-Per ... Vedo che l'ordine suggerito è, in effetti, da sinistra a destra come il tuo codice ha. Tuttavia, dai nostri registri vedo che ci sono molti casi in cui questo non è rispettato dai proxy in natura e l'indirizzo IP che si desidera verificare potrebbe trovarsi a entrambe le estremità dell'elenco.
Chris Withers,

1
O nel mezzo, come accadrebbe se alcuni delegati rispettassero l'ordine da sinistra a destra e altri no.
Brilliand,

0

Grazie per questo, molto utile.

Sarebbe di aiuto se il codice fosse sintatticamente corretto. Siccome c'è un {troppi attorno alla linea 20. Temo che nessuno lo abbia effettivamente provato.

Potrei essere pazzo, ma dopo averlo provato su alcuni indirizzi validi e non validi, l'unica versione di validate_ip () che ha funzionato è stata questa:

    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

Ecco una versione modificata se si utilizzano i servizi del livello di memorizzazione nella cache di 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;
}

0

Solo una versione VB.NET della risposta:

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

3
C'è un tag "PHP" nella domanda
luchaninov,

0

Solo un altro modo pulito:

  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

Dalla classe Request di 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);
}

Proprietà non definita: $ server
C47

0

Sono sorpreso che nessuno abbia menzionato filter_input, quindi ecco la risposta di Alix Axel condensata su una riga:

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

-1

Hai praticamente risposto alla tua domanda! :)

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

fonte


-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);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.