¿Cuál es la forma más precisa de recuperar la dirección IP correcta de un usuario en PHP?

301

Sé que hay una gran cantidad de encabezados de variables $ _SERVER disponibles para la recuperación de direcciones IP. Me preguntaba si hay un consenso general sobre cómo recuperar con mayor precisión la dirección IP real de un usuario (sabiendo que ningún método es perfecto) utilizando dichas variables.

Pasé algún tiempo tratando de encontrar una solución en profundidad y se me ocurrió el siguiente código basado en varias fuentes. Me encantaría que alguien pudiera hacer agujeros en la respuesta o arrojar algo de luz sobre algo tal vez más preciso.

editar incluye optimizaciones 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;
 }

Palabras de advertencia (actualización)

REMOTE_ADDRtodavía representa la fuente más confiable de una dirección IP. Las otras $_SERVERvariables mencionadas aquí pueden ser suplantadas por un cliente remoto muy fácilmente. El propósito de esta solución es intentar determinar la dirección IP de un cliente sentado detrás de un proxy. Para sus propósitos generales, puede considerar usar esto en combinación con la dirección IP devuelta directamente $_SERVER['REMOTE_ADDR']y almacenar ambos.

Para el 99.9% de los usuarios, esta solución se adaptará perfectamente a sus necesidades. No lo protegerá del 0.1% de los usuarios maliciosos que buscan abusar de su sistema al inyectar sus propios encabezados de solicitud. Si confía en las direcciones IP para algo crítico, recurra aREMOTE_ADDR y no se moleste en atender a quienes están detrás de un proxy.

Corey Ballou
fuente
2
Para la pregunta de whatismyip.com, creo que hacen algo como este script, ¿lo estás ejecutando localmente? Si es por eso que tiene una IP interna, en ese caso no se envía nada a través de la interfaz pública, por lo que no hay información para que obtenga php
Matt
2
Asegúrese de tener esto en cuenta al implementar esto: stackoverflow.com/questions/1672827/…
Kevin Peno
19
Recuerde que todos estos encabezados HTTP son realmente fáciles de modificar: con su solución, solo tengo que configurar mi navegador para enviar un encabezado X-Fordered-For con una IP aleatoria y su script devolverá una dirección falsa. Entonces, dependiendo de lo que intente hacer, esta solución podría ser menos confiable que simplemente usar REMOTE_ADDR.
gnomnain
14
OMFG, "IP no confiable"! La primera vez que veo esas tonterías aquí en SO. La única dirección IP confiable es REMOTE_ADDR
su sentido común el
3
-1 esto es vulnerable a la suplantación de identidad. Todo lo que está haciendo es preguntarle al usuario cuál debería ser su dirección IP.
torre

Respuestas:

269

Aquí hay una forma más corta y limpia de obtener la dirección 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;
                }
            }
        }
    }
}

¡Espero que ayude!


Su código parece estar bastante completo ya, no puedo ver ningún posible error en él (aparte de las advertencias de IP habituales), aunque cambiaría la validate_ip()función para confiar en la extensión del filtro:

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

También su HTTP_X_FORWARDED_FORfragmento se puede simplificar de esto:

// 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 esto:

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

También es posible que desee validar las direcciones IPv6.

Alix Axel
fuente
44
Definitivamente aprecio la filter_varsolución, ya que elimina un montón de comprobaciones int no firmadas de la dirección IP. También me gusta el hecho de que también me da la opción de validar las direcciones IPv6. La HTTP_X_FORWARDED_FORoptimización también es muy apreciada. En unos minutos actualizaré el código.
Corey Ballou el
33
-1 esto es vulnerable a la suplantación de identidad, todo lo que está haciendo es preguntar al usuario cuál debería ser su dirección IP.
torre
77
@Rook: Sí, lo sé. El OP lo sabe, y también lo he mencionado en mi respuesta. Pero, gracias por el comentario.
Alix Axel
1
FYI: Tuve que eliminar FILTER_FLAG_IPV6 para que el código de Alix Axel funcione.
darkAsPitch
2
@ rubenrp81 El controlador de socket TCP es la única fuente canónica, todo lo demás está controlado por el atacante. El código anterior es el sueño de un atacante.
torre
12

Aun así, sin embargo, obtener la dirección IP real de un usuario no será confiable. Todo lo que necesitan hacer es usar un servidor proxy anónimo (uno que no respete los encabezados de http_x_forwards_for, http_forwards, etc.) y todo lo que obtendrá es la dirección IP de su servidor proxy.

Luego puede ver si hay una lista de direcciones IP del servidor proxy que son anónimas, pero no hay forma de asegurarse de que sea 100% precisa y lo máximo que puede hacer es hacerle saber que es un servidor proxy. Y si alguien está siendo inteligente, puede falsificar encabezados para reenviar HTTP.

Digamos que no me gusta la universidad local. Averiguo qué direcciones IP registraron, y hago que su dirección IP sea prohibida en su sitio haciendo cosas malas, porque creo que honran los reenvíos HTTP. La lista es interminable.

Luego, como adivinó, hay direcciones IP internas, como la red universitaria que mencioné anteriormente. Muchos usan el formato 10.xxx. Entonces, todo lo que sabría es que se reenvió para una red compartida.

Entonces no comenzaré mucho en eso, pero las direcciones IP dinámicas ya no son la banda ancha. Entonces. Incluso si obtiene una dirección IP de usuario, espere que cambie en 2 a 3 meses, como máximo.

Peter Mortensen
fuente
Gracias por el aporte. Actualmente estoy utilizando la dirección IP del usuario para ayudar en la autenticación de la sesión utilizando su IP de clase C como un factor limitante para limitar el secuestro de la sesión pero permitir IP dinámicas dentro de lo razonable. IPs falsas y servidores proxy anónimos es algo con lo que tendré que lidiar para un grupo selecto de individuos.
Corey Ballou
@cballou - Seguramente para este propósito REMOTE_ADDR es el correcto para usar. Cualquier enfoque que se base en encabezados HTTP es vulnerable a la suplantación de encabezados. ¿Cuánto dura una sesión? Las IP dinámicas no cambian rápidamente.
MZB
Lo hacen, especialmente si quiero que lo hagan (cambiar la dirección mac que admiten muchos controladores). Solo REMOTE_ADDR por sí solo es suficiente para obtener el último servidor con el que habló. Entonces, en una situación de proxy, obtiene la IP del proxy.
8

Usamos:

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

La explosión en HTTP_X_FORWARDED_FOR se debe a problemas extraños que tuvimos al detectar direcciones IP cuando se utilizó Squid .

gabrielk
fuente
Vaya, me acabo de dar cuenta de que básicamente haces lo mismo con explotar, y así sucesivamente. Además de un poco más. Así que dudo que mi respuesta fue de mucha ayuda. :)
gabrielk
Esto devuelve la dirección del localhost
Scarl
3

Mi respuesta es básicamente una versión pulida, totalmente validada y completamente empaquetada de la respuesta 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 = '';
    };

?>

Cambios:

  • Simplifica el nombre de la función (con el estilo de formato 'camelCase').

  • Incluye una verificación para asegurarse de que la función no esté declarada en otra parte de su código.

  • Tiene en cuenta la compatibilidad 'CloudFlare'.

  • Inicializa múltiples nombres de variables "relacionadas con IP" en el valor devuelto, de la función 'getClientIP'.

  • Asegura que si la función no devuelve una dirección IP válida, todas las variables se configuran en una cadena vacía, en lugar de null.

  • Son solo (45) líneas de código.

James Anderson Jr.
fuente
2

La pregunta más importante es ¿para qué?

Su código es casi tan completo como podría ser, pero veo que si ve lo que parece un encabezado agregado de proxy, usa ese INSTEAD del CLIENT_IP, sin embargo, si desea esta información para fines de auditoría, tenga en cuenta que es muy fácil falsificar.

Ciertamente, nunca debe usar direcciones IP para ningún tipo de autenticación, incluso estas pueden ser falsificadas.

Puede obtener una mejor medición de la dirección IP del cliente empujando un applet flash o java que se conecta de nuevo al servidor a través de un puerto no http (que por lo tanto revelaría proxies transparentes o casos en los que los encabezados inyectados por proxy son falsos, pero tenga en cuenta que, cuando el cliente SOLO puede conectarse a través de un proxy web o el puerto de salida está bloqueado, no habrá conexión desde el applet.

C.

symcbean
fuente
Teniendo en cuenta que estoy buscando una solución solo para PHP, ¿sugiere que agregue $_SERVER['CLIENT_IP']como segunda declaración if?
Corey Ballou el
No, solo que si desea dar algún significado a los datos devueltos, sería una buena idea preservar la dirección del punto final de la red (IP del cliente), así como cualquier cosa que sugiera un valor diferente en los encabezados de proxy agregado (por ejemplo, puede ver muchas direcciones 192.168.1.x pero provenientes de diferentes clientes ips) C.
symcbean
1

Me doy cuenta de que hay respuestas mucho mejores y más concisas arriba, y esta no es una función ni el guión más elegante. En nuestro caso, necesitábamos generar tanto el spoofable x_forwards_for como el remote_addr más confiable en un conmutador simplista por decir. Necesitaba permitir espacios en blanco para inyectar en otras funciones if-none o if-singular (en lugar de simplemente devolver la función preformateada). Necesitaba una var "activada o desactivada" con etiquetas personalizadas por interruptor para la configuración de la plataforma. También necesitaba una forma para que $ ip fuera dinámico dependiendo de la solicitud, de modo que tomara la forma de forward_for.

Además, no vi a nadie dirección isset () vs! Empty (): es posible ingresar nada para x_forward_for todavía desencadenar la verdad isset () que resulta en var en blanco, una forma de evitarlo es usar && y combinar ambos como condiciones. Tenga en cuenta que puede falsificar palabras como "PWNED" como x_forwards_for, así que asegúrese de esterilizar a una sintaxis IP real si su salida está en algún lugar protegido o en DB.

Además, puede probar usando el traductor de Google si necesita un proxy múltiple para ver la matriz en x_forwarder_for. Si quieres SPOOF cabeceras de prueba, mira esto Chrome cliente Cabecera Spoof extensión de . Esto se configurará de manera predeterminada solo como remote_addr estándar mientras esté detrás de un proxy proxy.

No conozco ningún caso en el que remote_addr pueda estar vacío, pero está allí como reserva por si acaso.

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

Para hacer que estas dinámicas se usen en funciones o consultas / eco / vistas a continuación, digamos para informes de generación de errores o errores, use globals o simplemente emítelos donde desee sin hacer un montón de otras condiciones o salida de esquema estático funciones

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

Gracias por todos tus buenos pensamientos. Por favor, avíseme si esto podría ser mejor, todavía un poco nuevo para estos encabezados :)

dhaupin
fuente
1

Se me ocurrió esta función que no solo devuelve la dirección IP sino una matriz con información 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;
}

Aquí está la función:

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

Como alguien dijo anteriormente, la clave aquí es por qué razón desea almacenar los ips del usuario.

Daré un ejemplo de un sistema de registro en el que trabajo y, por supuesto, la solución solo para contribuir algo en esta vieja discusión que aparece con frecuencia en mis búsquedas.

Muchas bibliotecas de registro de php usan ip para limitar / bloquear intentos fallidos en función de la ip del usuario. Considere esta tabla:

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

Luego, cuando un usuario intenta hacer un inicio de sesión o cualquier cosa relacionada con el servicio, como un restablecimiento de contraseña, se llama a una función al inicio:

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

Digamos, por ejemplo, $this->token->get('attempts_before_ban') === 10y 2 usuarios vienen por los mismos ips como es el caso en los códigos anteriores donde los encabezados pueden ser falsificados , luego, después de 5 intentos, ambos están prohibidos ! Peor aún, si todos provienen del mismo proxy, ¡solo se registrarán los primeros 10 usuarios y se prohibirá el resto!

Lo crítico aquí es que necesitamos un índice único en la tabla attemptsy podemos obtenerlo de una combinación como:

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

donde jwt_loadproviene de una cookie http que sigue la tecnología de token web json donde almacenamos solo la carga cifrada que debe contener un valor arbitrario / único para cada usuario. Por supuesto, la solicitud debe modificarse a: "SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ? AND jwt_load = ?"y la clase también debe iniciar a private $jwt.

centurias
fuente
0

Me pregunto si tal vez deberías iterar sobre el explotado HTTP_X_FORWARDED_FOR en orden inverso, ya que mi experiencia ha sido que la dirección IP del usuario termina al final de la lista separada por comas, así que al comenzar al comienzo del encabezado, estás es más probable que se devuelva la dirección IP de uno de los servidores proxy, lo que podría permitir el secuestro de sesión ya que muchos usuarios pueden acceder a través de ese servidor proxy.

Chris Withers
fuente
1
Después de leer la página de Wikipedia en HTTP_X_FORWARDED_FOR: en.wikipedia.org/wiki/X-Forwards-For ... Veo que el orden sugerido es, de hecho, de izquierda a derecha como lo tiene su código. Sin embargo, de nuestros registros puedo ver que hay muchos casos en los que esto no es respetado por los proxies en la naturaleza y la dirección IP que desea verificar podría estar al final de la lista.
Chris Withers
1
O en el medio, como sucedería si algunos de los representantes respetaran el orden de izquierda a derecha y otros no.
Brilliand
0

Gracias por esto, muy útil.

Sin embargo, ayudaría si el código fuera sintácticamente correcto. Tal como está, hay un {demasiados en la línea 20. Lo cual me temo que significa que en realidad nadie lo intentó.

Puedo estar loco, pero después de probarlo en algunas direcciones válidas e inválidas, la única versión de validate_ip () que funcionó fue esta:

    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
fuente
0

Aquí hay una versión modificada si usa los servicios de capa de almacenamiento en caché de 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
fuente
0

Solo una versión VB.NET de la respuesta:

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
Ábaco
fuente
3
Hay una etiqueta "PHP" en la pregunta
luchaninov
0

Solo otra forma limpia:

  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
fuente
0

Desde la clase Solicitud 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
fuente
Propiedad no
C47
0

Me sorprende que nadie haya mencionado filter_input, así que aquí está la respuesta de Alix Axel resumida en una línea:

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
fuente
-1

¡Has contestado tu propia pregunta! :)

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

Fuente

Alex Weber
fuente
-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
fuente