记录一下phpBB3对反向代理中的IP处理机制
处理几个phpBB3项目迁移, 部分运行场景转移到内网, 需要外网通过nginx/openresty之类的网关反向代理进行访问. 在网关处已经正确配置了远端IP的转发
location / {
proxy_pass http://your_brilliant_website/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_intercept_errors on;
}
在运行phpBB3的服务器上, 是通过Nginx + php-fpm 的方式处理PHP访问的, 经检查已经在$_SERVER中能看到正确的远端IP
$_SERVER['HTTP_X_FORWARDED_FOR'] 11.23.212.66
$_SERVER['HTTP_REMOTE_HOST'] 11.23.212.66
$_SERVER['HTTP_X_REAL_IP'] 11.23.212.66
但是在phpBB3中, sessions表里记录的IP依然是网关的内网IP.
查看了一下代码, 关联的代码在 forum/phpbb/session.php. IP部分取自$_SERVER['REMOTE_ADDR'] 仅对值进行了一些判断和处理. 注释里也说明了这样做的原因: forward_for太容易伪造.
$ip = htmlspecialchars_decode($request->server('REMOTE_ADDR'));
$ip = preg_replace('# {2,}#', ' ', str_replace(',', ' ', $ip));
$vars = array('ip');
extract($phpbb_dispatcher->trigger_event('core.session_ip_after', compact($vars)));
$ips = explode(' ', trim($ip));
$this->ip = '127.0.0.1';
foreach ($ips as $ip)
{
if (function_exists('phpbb_ip_normalise'))
{
$ip = phpbb_ip_normalise($ip);
if (empty($ip))
{
break;
}
$this->ip = $ip;
continue;
}
if (preg_match(get_preg_expression('ipv4'), $ip))
{
$this->ip = $ip;
}
else if (preg_match(get_preg_expression('ipv6'), $ip))
{
if (stripos($ip, '::ffff:') === 0)
{
$ipv4 = substr($ip, 7);
if (preg_match(get_preg_expression('ipv4'), $ipv4))
{
$ip = $ipv4;
}
}
$this->ip = $ip;
}
else
{
break;
}
}
但是phpBB3也增加了对forwarded_for部分的记录和判断, 因此增加了一个字段forwarded_for , 以及对应的配置项forwarded_for_check , 这一项在管理员控制面板的"综合->安全设置->经过验证的 X_FORWARDED_FOR 字段头"中设置.
if ($config['forwarded_for_check'])
{
$this->forwarded_for = preg_replace('# {2,}#', ' ', str_replace(',', ' ', $this->forwarded_for));
$ips = explode(' ', $this->forwarded_for);
foreach ($ips as $ip)
{
if (!empty($ip) && !preg_match(get_preg_expression('ipv4'), $ip) && !preg_match(get_preg_expression('ipv6'), $ip))
{
$this->forwarded_for = '';
break;
}
}
}
else
{
$this->forwarded_for = '';
}
开启后, 会在forwarded_for中记录这个IP, 并对这个IP启用封禁规则.
参考
- 这里有对使用
$_SERVER['HTTP_X_FORWARDED_FOR'] 和$_SERVER['REMOTE_ADDR'] 的安全性的详细讨论 https://stackoverflow.com/questions/3003145/how-to-get-the-client-ip-address-in-php
|