lushaoyan

现状

目前主流的函数方法:

<?php
function getIp()
{
    if ($_SERVER["HTTP_CLIENT_IP"] && strcasecmp($_SERVER["HTTP_CLIENT_IP"], "unknown")) {
        $ip = $_SERVER["HTTP_CLIENT_IP"];
    } else {
        if ($_SERVER["HTTP_X_FORWARDED_FOR"] && strcasecmp($_SERVER["HTTP_X_FORWARDED_FOR"], "unknown")) {
            $ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
        } else {
            if ($_SERVER["REMOTE_ADDR"] && strcasecmp($_SERVER["REMOTE_ADDR"], "unknown")) {
                $ip = $_SERVER["REMOTE_ADDR"];
            } else {
                if (isset ($_SERVER[\'REMOTE_ADDR\']) && $_SERVER[\'REMOTE_ADDR\'] && strcasecmp($_SERVER[\'REMOTE_ADDR\'],
                        "unknown")
                ) {
                    $ip = $_SERVER[\'REMOTE_ADDR\'];
                } else {
                    $ip = "unknown";
                }
            }
        }
    }
    return ($ip);
}

echo getIp();

测试

curl伪造IP请求:

$ch = curl_init(\'http://localhost/ip.php\');

//通用设置
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);//不直接输出
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);//跟踪重定向

//伪造请求头
$ip = mt_rand(1, 255) . \'.\' . mt_rand(1, 255) . \'.\' . mt_rand(1, 255) . \'.\' . mt_rand(1, 255);
$header = [
    \'CLIENT-IP: \' . $ip,
    \'X-FORWARDED-FOR: \' . $ip,
    \'X-REAL-IP: \' . $ip,
    \'Accept-Language: zh-CN,zh;\',
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);

$html = curl_exec($ch);
curl_close($ch);
echo $html;

输出SERVER数组,发现【HTTP_CLIENT_IP】、【HTTP_X_FORWARDED_FOR】、【HTTP_X_REAL_IP】是随机变动的IP地址。

主流方法根本不安全!

分析

为什么?

HTTP_CLIENT_IP:存在于http请求的header
HTTP_X_FORWARDED_FOR:请求转发路径,客户端IP,代理1IP,代理2IP......
HTTP_X_REAL_IP:这个用得比较少,暂不讨论。

这三个值都是从HTTP请求头获取的,所以并不可靠!

REMOTE_ADDR

REMOTE_ADDR:是直接从TCP中获取的IP,基本不会被伪造!

返回查看$_SERVER数组,发现【REMOTE_ADDR】显示正确的IP!

所以直接用 $_SERVER[\'REMOTE_ADDR\'] 就解决问题了?

其实还不行,如果客户端和服务器之间存在代理服务器,【REMOTE_ADDR】的值是最后一个代理服务器的IP!

只有第一台接收客户端请求的代理服务器的【REMOTE_ADDR】值才是客户的真实IP地址,要把该值传递下去!

解决方案

1. 客户端和服务器直连

<?php
function get_client_ip()
{
    $ip = $_SERVER[\'REMOTE_ADDR\'];
    return $ip;
}

2. 客户端和服务器存在中间代理

第一层nginx代理设置:

proxy_set_header X-Forwarded-For $remote_addr;

其他层nginx代理设置:

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

PHP代码:

<?php
function get_client_ip()
{
    $ip = null;
    if (isset($_SERVER[\'HTTP_X_FORWARDED_FOR\'])) {
        $ip = explode(\',\', $_SERVER[\'HTTP_X_FORWARDED_FOR\']);
        $ip = trim(current($ip));
    }
    return $ip;
}

分类:

技术点:

相关文章: