- 更新:2020-09-16 15:40:23
- 首发:2020-08-14 20:26:12
- 教程
- 6328
PHP获取客户端真实IP地址,需要根据具体的服务器环境来确定使用哪种方法。目前搜索到的方法,大多是直接贴代码,没有针对不同情况作出说明,有可能导致系统被假IP骗过(IP欺骗)。
很多文章都提到“无法保证获取到的访客IP地址100%准确”,是否意味着PHP获取访客IP一定有漏洞可钻呢?
只要根据实际部署情况选择相对应的代码获取访客IP地址,是可以确保程序不被假IP欺骗的。
PHP的运行方式
PHP支持非常多的运行方式,例如php-cgi、php-fpm、swoole、php-cli、php-mod等。其中,php-fpm是php的fast-cgi的进程管理器。php-mod通常配合Apache使用,而php-fpm通常配合Nginx使用。从PHP5.4开始,PHP甚至可以以内置PHP服务(Buid-in web server)方式运行。逐渐的,PHP形成了很多经典搭配,例如LAMP、LNMP、LNMPA、IIS+PHP。
无论采用哪种PHP运行方式,分清是谁传递IP给PHP程序即可。这里对几种常见环境进行分析。
无代理层(PHP内置服务器/swoole)
由于客户端IP数据是从TCP/IP协议层传递过来的,因此在没有中间代理(客户端和服务器直连)的情况下,可以直接通过标准方法REMOTE_ADDR
获得与PHP直接通讯的IP地址。
例如$_SERVER['REMOTE_ADDR']
或getenv("REMOTE_ADDR")
。
而在swoole
中,可以通过$request->server['remote_addr']
获得客户端IP地址。
在这种情况下,REMOTE_ADDR
不可以显式的伪造,获取到的是实际与服务器连接的IP地址,是可靠的。
Nginx代理(Nginx + php-fpm / Nginx + swoole)
常见的LNMP方案,属于Nginx反向代理。Nginx与php的通讯,无论unix socket
方式还是tcp socket
方式,都跟Nginx的Header配置有关系。
proxy_http_version 1.1;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
可以在第一层nginx代理设置 proxy_set_header X-Forwarded-For $remote_addr
,
其他层nginx代理设置proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for
这样就可以通过X-Forwarded-For
获取客户端真实IP地址。
如果设置了X-Real-IP $remote_addr
,则通过X-Real-IP
获取客户端IP地址。
PHP代码:
// php-fpm等
$_SERVER['HTTP_X_FORWARDED_FOR']);
// swoole
$this->http_input->header('x-real-ip');
总结来说,当有Nginx
代理的情况下,需要根据具体配置来选择获取的Header,从而正确获取客户端IP地址,此时PHP中的的REMOTE_ADDR
可能是最后一级代理的IP地址。
Apache代理(Apache + php_mod / Apache + php-fpm)
通常在Apache + PHP方案中,获取IP地址取决于Apache配置信息。
绝大部分情况可以使用$_SERVER["REMOTE_ADDR"]
获取到真实客户端IP地址。
如果Apache上级存在Nginx,那么可以在Apache中使用mod_rpaf
模块将客户端IP地址传递到X-Forwarded-For
头中。
PAFheader X-Forwarded-For
负载均衡/云虚拟机/Serverless
在负载均衡条件下,需要查阅对应负载均衡程序的文档,来决定使用哪种方法获取真实客户端IP地址。
在大部分虚拟主机、负载均衡及无服务器中,可以通过HTTP_CLIENT_IP
、HTTP_X_FORWARDED_FOR
或X-REAL-IP
其一得知客户端IP地址。但是不建议对多种来源进行空值判断,这样容易被伪造者利用,从而实现IP欺骗。
目前并未有标准规定将客户端IP地址放入名为CLIENT_IP
的环境变量,但是有不少老虚拟主机供应商这样做。新的提供商都不再使用该环境变量。网上大量的PHP文章中都是优先获取HTTP_CLIENT_IP
的值,因此导致网上存在大量的服务器可以被伪造IP欺骗。
可能有该漏洞的代码:
<?php
if(!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown')) {
$ip = $_SERVER['REMOTE_ADDR'];
}
IIS + PHP
这种情况下直接使用$_SERVER['REMOTE_ADDR']
方法即可。
总结
无论是哪种情况,如果用户使用匿名代理访问服务器,只能获取到代理服务器的IP地址,其IP地址仍然具有参考意义。
获取客户端真实IP地址,此事需要查阅相关资料根据具体情况选用某一个方法,而不是复制粘贴。
部分方法获取到的值可能是一个数组。
不建议对获取到的IP地址进行正则过滤,有可能你获取到的是一个IPv6地址。
感觉php比asp好用多了
嗯,是呢
👍
请问,这个发表评论编辑器用的什么?感觉不错。
老师你好,我希望能用一个openwrt路由器实现IPv4和IPv6的桥接,请问我该如何实现?我尝试了直接新增dhcpv6的接口,但是效果不甚理想(无法成功获取公网的ipv6,但是直连上级路由的其他设备是可以获取公网的ipv6地)
![%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE20241205230845.png](https://cdn.wyr.me/visitor-files/2024-12-05/1733411344287屏幕截图 2024-12-05 230845.png)你好
,为什么我这里是0039 813C 0600 0075 16xx xx xx,只有前6组是相同的,博客中要前8位相同,这个不同能不能照着修改呢?我系统版本是Win1124H2
大神你好,win11专业版24h2最新版26100.2033,文件如何修改?谢谢
win11专业版24h2最新版26100.2033,Windows Feature Experience Pack 1000.26100.23.0。C:\Windows\System32\termsrv.dll系统自带的这个文件,39 81 3C 06 00 00 0F 85 XX XX XX XX 替换为 B8 00 01 00 00 89 81 38 06 00 00 90。仍然无法远程连接。原来是win11 21h2系统,是可以远程链接的。共享1个主机,2个显示器,2套键鼠,各自独立操作 各自不同的账号,不同的桌面环境。
博主,win11专业版24h2最新版,C:\Windows\System32\termsrv.dll系统自带的这个文件,找不到应该修改哪个字段。我的微信:一三五73二五九五00,谢谢