关于 PHP 获取 IP 的方法与安全问题

该文章根据 CC-BY-4.0 协议发表,转载请遵循该协议。
本文地址:https://fenying.net/post/2014/04/27/how-to-get-client-ip-in-php/

今日为学校开发网站时,要加入限制外网 IP 段访问网站的功能。由于网站使用的是一个建站程序,我直接使用了其中内置的 getip 函数获取客户端 IP,然后根据 IP 网段限制访问。

但是在这过程中,一开始看似正常,然后就有多位管理跟我说他用外部网络仍然可以访问网站。我起初在检测 IP 网段分析算法,但是发现毫无问题。最后终于在 getip 函数里发现了问题。

getip 函数的实现是,先检测 $_SERVER['HTTP_X_FORWARDED_FOR'] 是否有值,有即返回该值,否则返回 $_SERVER['REMOTE_ADDR']

问题就出在这里了。这个函数的设计原意是好的,该作者企图通过 $_SERVER['HTTP_X_FORWARDED_FOR'] 获取通过代理 IP 访问网站的用户的真实 IP,奈何这个值是可以造假的!

我上百度查了下资料,原来 $_SERVER['HTTP_X_FORWARDED_FOR'] 是包含在 HTTP Request Header 里的内容,也就是说,你可以随意在 HTTP Request Header 里加入这个参数,然后用上面的 getip 函数获取的都是你指定的 IP!这样伪造 IP 实在太简单了。

而且,在我遇到的问题里,更为严重,因为可以伪造 IP(或许用户不知道,但是部分手机浏览器已经自动做了),那么我设置的内网限制也就毫无意义了!我只要在 HTTP 请求里加入 HTTP_X_FORWARDED_FOR: 10.10.10.10,那么我用 getip 获取的就是 10 网段,也就是内网网段!

在我把 getip() 直接换成 $_SERVER['REMOTE_ADDR'] 以后,问题迎刃而解。

所以,我个人的建议是,如果你的程序需要对 IP 网段进行限制,那么请直接使用 $_SERVER['REMOTE_ADDR'] ,而绝不要尝试检测代理 IP,因为那是无用功。(具体参看参考文献)

【参考文献】

comments powered by Disqus