前言

最近在工作中做一个投票功能被刷票了。

虽然这个刷票的bug不是今天文中要说的这个,但这个问题却是在后续发现的bug,并且很多网上的方法及开源框架中都有这个问题存在,鉴于问题的普遍性,这里有必要说一下。

错误姿势

以下是一个随处可见的获取客户端ip的php函数:

function ip()
{
    if (getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), 'unknown')) {
        $ip = getenv('HTTP_CLIENT_IP');
    } elseif (getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), 'unknown')) {
        $ip = getenv('HTTP_X_FORWARDED_FOR');
    } elseif (getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'), 'unknown')) {
        $ip = getenv('REMOTE_ADDR');
    }
    return preg_match('/[\d\.]{7,15}/', $ip, $matches) ? $matches [0] : '';
}

先读HTTP_CLIENT_IP,如果没有,再读HTTP_X_FORWARDED_FOR,还没有,最后读REMOTE_ADDR

存在的问题

有什么问题呢?

由于HTTP_CLIENT_IPHTTP_X_FORWARDED_FOR是从HTTP头里面读取出来的,所以,他们是可以伪造的!

伪造IP

们可以这样伪造这两个值:

$ip = '111.222.111.222';
$header = array(
    "CLIENT-IP:{$ip}",
    "X-FORWARDED-FOR:{$ip}",
);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);

那么,用这个获取ip的函数用来限制ip投票就变得毫无意义!

正确姿势

其实们获取客户端ip只需要用

$_SERVER['REMOTE_ADDR']

就可以了,REMOTE_ADDR是在TCP连接的时候设定的,是无法进行伪造的。虽然他可能是代理服务器的IP,但无论如何,总比一个可以随意伪造的IP安全得多。

如果您觉得您在我这里学到了新姿势,博主支持转载,姿势本身就是用来相互学习的。同时,本站文章如未注明均为 hisune 原创 请尊重劳动成果 转载请注明 转自: 关于php的获取客户端IP和伪造客户端IP的若干事 - hisune.com