PHP如何获取反向代理下真实访客的IP
前阵子受到羊毛党的报复,对我们短信接口发起攻击。
分析LOG发现有几千个IP,我就不相信他有这么多肉鸡……怀疑我们获取到的IP是伪造不真实的IP。
IP有这么好伪造吗?那就视乎你怎么获取IP了,看THINKPHP获取IP的代码,主要是通过下面的环境变量获取:
REMOTE_ADDR
HTTP_X_FORWARDED_FOR
HTTP_CLIENT_IP
/**
* 获取客户端IP地址
* @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字
* @param boolean $adv 是否进行高级模式获取(有可能被伪装)
* @return mixed
*/
public function ip($type = 0, $adv = false)
{
$type = $type ? 1 : 0;
static $ip = null;
if (null !== $ip) {
return $ip[$type];
}
if ($adv) {
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$pos = array_search('unknown', $arr);
if (false !== $pos) {
unset($arr[$pos]);
}
$ip = trim(current($arr));
} elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (isset($_SERVER['REMOTE_ADDR'])) {
$ip = $_SERVER['REMOTE_ADDR'];
}
} elseif (isset($_SERVER['REMOTE_ADDR'])) {
$ip = $_SERVER['REMOTE_ADDR'];
}
// IP地址合法验证
$long = sprintf("%u", ip2long($ip));
$ip = $long ? array($ip, $long) : array('0.0.0.0', 0);
return $ip[$type];
}
我们代码里面都是通过 $this->request->ip() 获取IP的……可能我们被羊毛骗了
再看看下面这个代码
HTTP_X_FORWARDED_FOR 和 HTTP_CLIENT_IP
伪造就是这么容易,这就是XFF欺骗
$url = 'http://www.fake.com/index.php';
// 参数
$data_string = '';
$URL_Info = parse_url($url);
$request = '';
if (!isset($URL_Info["port"])) {
$URL_Info["port"] = 80;
}
$request.="POST ".$URL_Info["path"]." HTTP/1.1\n";
$request.="Host: ".$URL_Info["host"]."\n";
$request.="Referer: ".$URL_Info["host"]."\n";
$request.="Content-type: application/x-www-form-urlencoded\n";
// HTTP_X_FORWARDED_FOR 的值,可以随心所欲
$request.="X-Forwarded-For:192.168.1.4\n";
// HTTP_CLIENT_IP 的值,可以随心所欲
$request.="client_ip:192.168.1.5\n";
$request.="Content-length: ".strlen($data_string)."\n";
$request.="Connection: close\n";
$request.="\n";
$request.=$data_string."\n";
$fp = fsockopen($URL_Info["host"], $URL_Info["port"]);
fputs($fp, $request);
fclose($fp);
了解到通过REMOTE_ADDR获取的IP是不能伪造,不过我们生产环境用了反向代理,架构类似下图
反向代理每个虚拟主机配置都加了这样的配置
proxy_set_header Host $host;
proxy_set_header X-Real-Ip $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
这样是可以转发 HTTP_X_FORWARDED_FOR 到RS服务器,不过 REMOTE_ADDR 还是代理的IP,不知道
proxy_set_header X-Real-Ip $remote_addr;
这个作用是什么,看着不是把 REMOTE_ADDR 转发到RS服务器,当时为了快速解决我把
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
改成
proxy_set_header X-Forwarded-For $remote_addr;
然后这样获取
$this->request->ip(0, true)
set_real_ip_from 10.10.10.10;
real_ip_header X-Forwarded-For;
PHP中$_SERVER没有走代理访问的时候是没有下面两个值的
[HTTP_X_FORWARDED_FOR] => 27.46.8.255
[HTTP_X_REAL_IP] => 27.46.8.255
[HTTP_X_FORWARDED_FOR] => 192.168.1.4, 27.46.8.255
[HTTP_X_REAL_IP] => 27.46.8.255
未完待续....