编 写:袁 亮 时 间:2014-05-20 说 明:关于ip的那些事 一、ip传递过程 1、【真实客户端】 ==> [多级代理服务器] ==> [CDN加速] ==> [前端代理nginx|squid] ==> 【apache】==> 【PHP】 2、中文括号的是必然会经过的,英文括号是可能经过的 3、标准的ip传递是REMOTE_ADDR和HTTP_X_FORWARDED_FOR,前一个跟当前服务连接的真实ip,后一个是请求到前一个ip之前,经过了哪些代理 4、REMOTE_ADDR是不可伪造的,HTTP_X_FORWARDED_FOR是可以任意修改的 5、按标准,每传递到下一层,都会将上一层的实际ip地址加入到HTTP_X_FORWARDED_FOR中,继续传递 6、对每一层来说,只有上一层的时间地址是可信的(REMOTE_ADDR),HTTP_X_FORWARDED_FOR均有风险 7、真实情况中,到了cdn或者前端代理之后,ip传递都是可信的(我们自己可控制),之前的都有篡改的危险 二、各服务的真实ip传递情况 1、CDN 快网的cdn会将用户的实际地址或者代理服务器地址传递到后面的服务中 $_SERVER["HTTP_USER_IP"] 【不用快网的时候可伪造】 $_SERVER["HTTP_FW_ADDR"] 【不用快网的时候可伪造】 测试了一个新的cdn测试,没有传递真实ip过来 2、nginx代理的情况下,可以使用x_real_ip来获取真实ip(有cdn的时候,该值获取的是cdn的ip地址) $_SERVER["HTTP_X_REAL_IP"] 【不用nginx的时候可伪造】 3、$_SERVER["HTTP_CLIENT_IP"] :代理服务器发送的客户端真实ip【可伪造】 三、现在使用的获取ip函数 a、如果有HTTP_CLIENT_IP,则该ip为用户ip(可被伪造) b、如果有HTTP_X_FORWARDED_FOR,则将HTTP_CLIENT_IP也加入到HTTP_X_FORWARDED_FOR,判断HTTP_X_FORWARDED_FOR中的ip是否是内网的,取第一个非内网的ip为客户端真实ip c、经过以上两步还没有取到ip的话,则根据REMOTE_ADDR取用户的ip ps:该函数的问题在于,前面两个的ip都是可以被任意伪造改写,从而导致获取不到用户的真实ip情况 四、附:(线上使用的获取ip函数) function getIp(){//获取IP函数 $ip = false; if(!empty($_SERVER["HTTP_CLIENT_IP"])){ $ip = $_SERVER["HTTP_CLIENT_IP"]; } if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ips = explode (", ", $_SERVER['HTTP_X_FORWARDED_FOR']); if ($ip) { array_unshift($ips, $ip); $ip = FALSE; } for ($i = 0; $i < count($ips); $i++) { if (!preg_match("/^(10|172\.16|192\.168)\./", $ips[$i])) { // 判断是否内网的IP $ip = $ips[$i]; break; } } } return ($ip ? $ip : $_SERVER['REMOTE_ADDR']); }