我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...

本文也是一篇水文,不过苏南大叔会稍稍给它写出一些深度来。文章中首先要说明一下thinkphp中获得ip的方法,然后描述一下通用的获得ip的方法及其漏洞,最后描述一下nginxphp配合使用的时候,获取ip的注意事项。

苏南大叔:thinkphp5如何获得访客IP?nginx传递ip的注意事项 - thinkphp-ip
thinkphp5如何获得访客IP?nginx传递ip的注意事项(图2-1)

本文测试环境:mac/php72/nginx/thinkphp@5.0.24

thinkphp5获得访客IP

thinkphp5自带的获得ip的代码,非常简单:

首先引入Request类包:

use think\Request;

然后初始化Request实例后,调用ip方法获得ip

$request = Request::instance();
$ip=$request->ip();

源文件的位置在thinkphp\library\think\Request.php:

public function ip($type = 0, $adv = true)
{
    $type      = $type ? 1 : 0;
    static $ip = null;
    if (null !== $ip) {
        return $ip[$type];
    }

    $httpAgentIp = Config::get('http_agent_ip');

    if ($httpAgentIp && isset($_SERVER[$httpAgentIp])) {
        $ip = $_SERVER[$httpAgentIp];
    } elseif ($adv) {
        if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
            $pos = array_search('unknown', $arr);
            if (false !== $pos) {
                unset($arr[$pos]);
            }
            $ip = trim(current($arr));
        } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
            $ip = $_SERVER['HTTP_CLIENT_IP'];
        } elseif (isset($_SERVER['REMOTE_ADDR'])) {
            $ip = $_SERVER['REMOTE_ADDR'];
        }
    } elseif (isset($_SERVER['REMOTE_ADDR'])) {
        $ip = $_SERVER['REMOTE_ADDR'];
    }
    // IP地址合法验证
    $long = sprintf("%u", ip2long($ip));
    $ip   = $long ? [$ip, $long] : ['0.0.0.0', 0];
    return $ip[$type];
}

源码的逻辑内容,大家自行理解。不过值得注意的是:上面这段thinkphp的代码,可以获取隐藏在自定义header头中的ip信息,这个思路看起来是很新颖不错,点赞。

苏南大叔:thinkphp5如何获得访客IP?nginx传递ip的注意事项 - thinkphp-ip
thinkphp5如何获得访客IP?nginx传递ip的注意事项(图2-2)

thinkphp3系列中,获得ip就一个函数,更加简单,get_client_ip()

网络流传的获取ip的函数

下面的php代码,是网络流传的获得ipphp函数。大家仔细看逻辑,就可以看到相关的漏洞所在。

function get_ip(){
    if (!empty($_SERVER["HTTP_CLIENT_IP"])) {
        $cip = $_SERVER["HTTP_CLIENT_IP"];
    } elseif (!empty($_SERVER["HTTP_X_FORWARDED_FOR"])) {
        $cip = $_SERVER["HTTP_X_FORWARDED_FOR"];
    } elseif (!empty($_SERVER["REMOTE_ADDR"])) {
        $cip = $_SERVER["REMOTE_ADDR"];
    } else {
        $cip = "--";
    }
    return $cip;
}

大家可以看到,这个ip的获取,是有elseif的,而其中的HTTP_X_FORWARDED_FOR,这个就是漏洞所在。如果相关的脚本,是去先检测HTTP_X_FORWARDED_FOR的话,那么就有一定的概率可以伪造ip了。这里,就涉及到匿名代理和透明代码的相关知识了,这里不做赘述。

thinkphp@3系列中的get_client_ip()中,也考虑到上述问题。下面是其定义:

/**
 * 获取客户端IP地址
 * @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字
 * @param boolean $adv 是否进行高级模式获取(有可能被伪装) 
 * @return mixed
 */
function get_client_ip($type = 0,$adv=false) {
    $type       =  $type ? 1 : 0;
    static $ip  =   NULL;
    if ($ip !== NULL) return $ip[$type];
    if($adv){
        if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $arr    =   explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
            $pos    =   array_search('unknown',$arr);
            if(false !== $pos) unset($arr[$pos]);
            $ip     =   trim($arr[0]);
        }elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
            $ip     =   $_SERVER['HTTP_CLIENT_IP'];
        }elseif (isset($_SERVER['REMOTE_ADDR'])) {
            $ip     =   $_SERVER['REMOTE_ADDR'];
        }
    }elseif (isset($_SERVER['REMOTE_ADDR'])) {
        $ip     =   $_SERVER['REMOTE_ADDR'];
    }
    // IP地址合法验证
    $long = sprintf("%u",ip2long($ip));
    $ip   = $long ? array($ip, $long) : array('0.0.0.0', 0);
    return $ip[$type];
}

配合nginx使用

众所周知,大多数时候,线上环境下,都是配合nginx来使用php的。而nginx对于php来说,就是个代理。所以,如果配置不当的情况下,就会恒定取到本机ip作为客户ip,这个就是很令人崩溃的事情了。

不过,这种情况,就需要nginxphp共同协商决定ip的判断标准了。

nginx配置ip:

server {
    listen       80;
    server_name  localhost;
    location / {
       root   html;
       index  index.html index.htm;
       proxy_pass                  http://backend;
       proxy_redirect              off;
       proxy_set_header            Host $host;
       proxy_set_header            X-Real-IP $remote_addr;
       proxy_set_header            X-Forwarded-For $proxy_add_x_forwarded_for;
   }
}

这里的X-Real-IP,就是目标header

php获得ip:

if (isset($_SERVER[`X-Real-IP`])) {
  $ip = $_SERVER[`X-Real-IP`];
}

总结

本文中,苏南大叔并没有推荐任何一种获取ip的方式,大家各自根据实际情况做出选择吧。

ip对于个人来说,就是一串数字罢了。但是在现实的网络环境中,ip是个集合,它包含了各个层面上的代理的结果。所以,对于获得ip这件事情来说,还是仔细思量一下比较好。因为ip的判断标准过于多样,那么这些取ip的逻辑,基本上都可能会存在着些漏洞。所以,使用这些函数方法前,请仔细想清楚。

下面的链接,是苏南大叔写的有关thinkphp的相关文章,敬请惠存:

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   php    IP