aLoNe IT Develop Studio

  • 首页
  • IT前沿
  • Web技术
  • 小姿势
  • 运维管理
  • 随笔
欢迎来到aLoNe.Adams.K的个人博客
  1. 首页
  2. Web技术
  3. 正文

ThinkPHP6中获取真实客户端IP需要注意的地方

2022年2月23日 4149点热度 10人点赞 0条评论

最近有个需求,需要对管理员登录使用IP白名单的方式来限制登录。在使用了TP6框架的系统中,可以很方便的使用request()->ip()来获取客户端IP。
但是当我测试时候发现,IP获取是有问题的,实际上获取的是本地地址127.0.0.1和::1这两个,经过仔细排查发现问题如下:
- 当使用nginx做了反向代理的时候,虽然正确设置了X-Real-IP和X-Forwarded-For等代理头,但是仍然无法获取到真实IP
- 观察TP6框架中的Request类中的ip方法,发现代码为

/**
     * 获取客户端IP地址
     * @access public
     * @return string
     */
    public function ip(): string
    {
        if (!empty(this->realIP)) {
            returnthis->realIP;
        }

        this->realIP =this->server('REMOTE_ADDR', '');

        // 如果指定了前端代理服务器IP以及其会发送的IP头
        // 则尝试获取前端代理服务器发送过来的真实IP
        proxyIp =this->proxyServerIp;
        proxyIpHeader =this->proxyServerIpHeader;

        if (count(proxyIp)>0 && count(proxyIpHeader) > 0) {
            // 从指定的HTTP头中依次尝试获取IP地址
            // 直到获取到一个合法的IP地址
            foreach (proxyIpHeader asheader) {
                tempIP =this->server(header);

                if (empty(tempIP)) {
                    continue;
                }

                tempIP = trim(explode(',',tempIP)[0]);

                if (!this->isValidIP(tempIP)) {
                    tempIP = null;
                } else {
                    break;
                }
            }

            // tempIP不为空,说明获取到了一个IP地址
            // 这时我们检查 REMOTE_ADDR 是不是指定的前端代理服务器之一
            // 如果是的话说明该 IP头 是由前端代理服务器设置的
            // 否则则是伪装的
            if (!empty(tempIP)) {
                realIPBin =this->ip2bin(this->realIP);

                foreach (proxyIp as ip) {serverIPElements = explode('/', ip);serverIP = serverIPElements[0];serverIPPrefix = serverIPElements[1] ?? 128;serverIPBin = this->ip2bin(serverIP);

                    // IP类型不符
                    if (strlen(realIPBin) !== strlen(serverIPBin)) {
                        continue;
                    }

                    if (strncmp(realIPBin,serverIPBin, (int) serverIPPrefix) === 0) {this->realIP = tempIP;
                        break;
                    }
                }
            }
        }

        if (!this->isValidIP(this->realIP)) {this->realIP = '0.0.0.0';
        }

        return $this->realIP;
    }
  • 观察代码发现,先通过REMOTE_ADDR获取了真实IP,但是此时因为通过nginx做了反代,实际上取到的值是nginx代理服务器的ip(因为我是本机反代,所以取到了本机地址)
  • 其次通过判断proxyServerIp和proxyServerIpHeader来进行有效性验证,这里比较关键的是proxyServerIp这个全局变量,通过查询代码可知,该变量全局未被复制,并且在TP框架其他地方也未进行赋值。但是他却在判断逻辑中充当重要依据,“如果获取到的真实IP是proxyServerIp存储的其中一个IP,则认为他是一个可信的反代服务器(因为其他方式获取到的信息都是可以被伪造的),那么此时可以获取其他字段中携带的客户端IP信息,否则就以REMOTE_ADDR获取到的ip作为真实ip”
  • 根据以上结果可以分析出,只需要对proxyServerIp这个变量进行赋值即可。那么此时可以在TP6框架中给用户自定义的Request类中,重新对该变量赋值即可,如下:
class Request extends think\Request
{
    protected $proxyServerIp = ['127.0.0.1','::1'];
}

至此,即可获取到有效客户端IP了。当然,你也可以进一步改造,使其可以通过配置文件进行读取。

标签: 暂无
最后更新:2022年2月23日

aLoNe.Adams.K

一只胖菜鸟!

点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复
文章分类
  • IT前沿 / 3篇
  • Web技术 / 2篇
  • 小姿势 / 24篇
  • 运维管理 / 5篇
  • 随笔 / 3篇
标签聚合
反向代理 Orange Pi 虚表 debian sqlite Yum google CentOS
最新 热点 随机
最新 热点 随机
团队开发规范培训(代码管理篇) 团队开发规范培训(序) 忆 我的座右铭 DevilBox全程操作记录 香橙派Orange Pi Zero基于Debian进行反向代理配置
我的座右铭 云服务器Linux下磁盘无损扩容注意事项! Thinkphp5开发中的坑及解决方案! 实在太邪恶!照亮妹纸绝对领域与私处 AES中Java加密PHP解密的坑。。。 忘带钥匙了? 没事,Google教你用眼睛开门
友情连接
  • 李建伟博客
  • 不忘初心的简书
归档
  • 2022年12月 / 5篇
  • 2022年6月 / 1篇
  • 2022年2月 / 3篇
  • 2020年11月 / 1篇
  • 2018年12月 / 1篇
  • 2018年5月 / 1篇
  • 2018年3月 / 1篇
  • 2017年11月 / 1篇
  • 2017年8月 / 1篇
  • 2017年3月 / 2篇
  • 2017年1月 / 1篇
  • 2016年9月 / 16篇

COPYRIGHT © 2022 aLoNe IT Develop Studio. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

陕ICP备19021656号-2