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_ADDR
todavía representa la fuente más confiable de una dirección IP. Las otras $_SERVER
variables 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.
fuente
Respuestas:
Aquí hay una forma más corta y limpia de obtener la dirección 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:También su
HTTP_X_FORWARDED_FOR
fragmento se puede simplificar de esto:A esto:
También es posible que desee validar las direcciones IPv6.
fuente
filter_var
solució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. LaHTTP_X_FORWARDED_FOR
optimización también es muy apreciada. En unos minutos actualizaré el código.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.
fuente
Usamos:
La explosión en HTTP_X_FORWARDED_FOR se debe a problemas extraños que tuvimos al detectar direcciones IP cuando se utilizó Squid .
fuente
Mi respuesta es básicamente una versión pulida, totalmente validada y completamente empaquetada de la respuesta de @ AlixAxel:
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.
fuente
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.
fuente
$_SERVER['CLIENT_IP']
como segunda declaración if?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.
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
Gracias por todos tus buenos pensamientos. Por favor, avíseme si esto podría ser mejor, todavía un poco nuevo para estos encabezados :)
fuente
Se me ocurrió esta función que no solo devuelve la dirección IP sino una matriz con información IP.
Aquí está la función:
fuente
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:
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:
Digamos, por ejemplo,
$this->token->get('attempts_before_ban') === 10
y 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
attempts
y podemos obtenerlo de una combinación como:donde
jwt_load
proviene 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 aprivate $jwt
.fuente
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.
fuente
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:
fuente
Aquí hay una versión modificada si usa los servicios de capa de almacenamiento en caché de CloudFlare
fuente
Solo una versión VB.NET de la respuesta:
fuente
Solo otra forma limpia:
fuente
Desde la clase Solicitud de Symfony https://github.com/symfony/symfony/blob/1bd125ec4a01220878b3dbc3ec3156b073996af9/src/Symfony/Component/HttpFoundation/Request.php
fuente
Me sorprende que nadie haya mencionado filter_input, así que aquí está la respuesta de Alix Axel resumida en una línea:
fuente
¡Has contestado tu propia pregunta! :)
Fuente
fuente
fuente