PHP获取真实IP(代理检测)
我确实可以跟踪用户的“真实”IP,如果他有一个发送真实 IP 标头的代理...是否有更好的解决方案,甚至更多标头?
由于这个函数在脚本中经常使用,所以它必须非常快,而且它似乎不在那个星座中:/
我提出了一些建议,但无法实现:
- 按顺序放置标题,什么是使用最多的“野外”,因此函数可以快速完成,
- 从而使 IP 的预匹配检测更快
===
function get_real_ip()
{
$proxy_headers = array(
'CLIENT_IP',
'FORWARDED',
'FORWARDED_FOR',
'FORWARDED_FOR_IP',
'HTTP_CLIENT_IP',
'HTTP_FORWARDED',
'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED_FOR_IP',
'HTTP_PC_REMOTE_ADDR',
'HTTP_PROXY_CONNECTION',
'HTTP_VIA',
'HTTP_X_FORWARDED',
'HTTP_X_FORWARDED_FOR',
'HTTP_X_FORWARDED_FOR_IP',
'HTTP_X_IMFORWARDS',
'HTTP_XROXY_CONNECTION',
'VIA',
'X_FORWARDED',
'X_FORWARDED_FOR'
);
foreach($proxy_headers as $proxy_header)
{
if(isset($_SERVER[$proxy_header]) && preg_match("/^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/", $_SERVER[$proxy_header])) /* HEADER ist gesetzt und dies ist eine gültige IP */
{
return $_SERVER[$proxy_header];
}
else if(stristr(',', $_SERVER[$proxy_header]) !== FALSE) /* Behandle mehrere IPs in einer Anfrage(z.B.: X-Forwarded-For: client1, proxy1, proxy2) */
{
$proxy_header_temp = trim(array_shift(explode(',', $_SERVER[$proxy_header]))); /* Teile in einzelne IPs, gib die letzte zurück und entferne Leerzeichen */
if(($pos_temp = stripos($proxy_header_temp, ':')) !== FALSE) $proxy_header_temp = substr($proxy_header_temp, 0, $pos_temp); /* Entferne den Port */
if(preg_match("/^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/", $proxy_header_temp) return $proxy_header_temp;
}
}
return $_SERVER['REMOTE_ADDR'];
}
I do get track the "real" IP of an user, if he has an proxy wich sends the header of the real IP... does any of have a better solution, or even more headers?
Since this function is used very often in the script, it has to be very fast, and it does not seem in that constellation :/
A few suggestions I came up with, but could not realise:
- put the headers in the order, what is used the most "in the wild", sothat the functions finishes fast
- making the pre_match-detecting for IP faster
===
function get_real_ip()
{
$proxy_headers = array(
'CLIENT_IP',
'FORWARDED',
'FORWARDED_FOR',
'FORWARDED_FOR_IP',
'HTTP_CLIENT_IP',
'HTTP_FORWARDED',
'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED_FOR_IP',
'HTTP_PC_REMOTE_ADDR',
'HTTP_PROXY_CONNECTION',
'HTTP_VIA',
'HTTP_X_FORWARDED',
'HTTP_X_FORWARDED_FOR',
'HTTP_X_FORWARDED_FOR_IP',
'HTTP_X_IMFORWARDS',
'HTTP_XROXY_CONNECTION',
'VIA',
'X_FORWARDED',
'X_FORWARDED_FOR'
);
foreach($proxy_headers as $proxy_header)
{
if(isset($_SERVER[$proxy_header]) && preg_match("/^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/", $_SERVER[$proxy_header])) /* HEADER ist gesetzt und dies ist eine gültige IP */
{
return $_SERVER[$proxy_header];
}
else if(stristr(',', $_SERVER[$proxy_header]) !== FALSE) /* Behandle mehrere IPs in einer Anfrage(z.B.: X-Forwarded-For: client1, proxy1, proxy2) */
{
$proxy_header_temp = trim(array_shift(explode(',', $_SERVER[$proxy_header]))); /* Teile in einzelne IPs, gib die letzte zurück und entferne Leerzeichen */
if(($pos_temp = stripos($proxy_header_temp, ':')) !== FALSE) $proxy_header_temp = substr($proxy_header_temp, 0, $pos_temp); /* Entferne den Port */
if(preg_match("/^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/", $proxy_header_temp) return $proxy_header_temp;
}
}
return $_SERVER['REMOTE_ADDR'];
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果代理发送标头,那么您可以获取客户端的原始 IP。如果代理没有,那么你就不能。
不幸的是(或者幸运的是,这取决于你的观点)就是这么简单。
我在我们的 Intranet 上所做的是将“intranet.mydomain.com”重定向到网络服务器上的“Intranet”,后者由于外部网络/DNS 配置而不使用代理......不知道你想要什么做,但这可能有用。
您还可以在浏览器中设置排除列表...
If the proxy sends a header then you can fetch the original IP of the client. If the proxy doesn't, then you can't.
Unfortunately (Or maybe fortunately depending on your perspective) it's as simple as that.
What I did at our intranet, is redirect "intranet.mydomain.com" to "intranet" on the webserver, the latter doesn't use the proxy due to out internal network/DNS configuration ... Don't know what you want to do, but this may be useful.
You can also set an exclude list in the browser...
对于 ipv6 地址,正则表达式验证将失败;所以我宁愿删除它(或者尝试找到更好的正则表达式)。
stripos($proxy_header_temp, ':')
也会导致意外的行为,例如“::1”(localhost,ipv6)。我的建议与上述修改:
the regex validation would fail for ipv6 addresses; so I would rather remove that (or try to find a better RegEX).
also
stripos($proxy_header_temp, ':')
would lead to a not expected behaivour for example for "::1" (localhost, ipv6).my suggestion with mentioned modifications: