如何使用PHP获取客户端IP地址?
我想记录访问我的网站的用户的IP地址。
如何使用PHP获取客户端IP地址?
我想记录访问我的网站的用户的IP地址。
$_SERVER['REMOTE_ADDR']
包含连接方的真实 IP 地址。这是您可以找到的最可靠的值。$_SERVER['HTTP_X_FORWARDED_FOR']
,但这个值很容易被欺骗。例如,它可以被没有代理的人设置,或者 IP 可以是代理后面 LAN 中的内部 IP。$_SERVER['HTTP_X_FORWARDED_FOR']
,请确保您还保存了 $_SERVER['REMOTE_ADDR']
值。例如,通过在数据库中保存两个不同字段中的两个值。REMOTE_ADDR
可能不会包含 TCP 连接的真实 IP。这完全取决于你的 SAPI。请确保你的 SAPI 被正确配置,以便 $_SERVER['REMOTE_ADDR']
实际返回 TCP 连接的 IP。如果未能做到这一点,可能会引发一些严重的漏洞,例如 StackExchange 曾经通过检查 REMOTE_ADDR
是否匹配“localhost”来授予管理员访问权限,不幸的是,SAPI 的配置…… - PacerierHTTP_X_FORWARDED_FOR
作为输入),允许非管理员通过更改HTTP_X_FORWARDED_FOR
头部获取管理员访问权限。另请参见http://blog.ircmaxell.com/2012/11/anatomy-of-attack-how-i-hacked.html。 - Pacerier$_SERVER['REMOTE_ADDR']
存放的可能不是实际的客户端 IP 地址,因为对于通过代理连接的客户端,它会给你一个代理地址。然而,这可能是您真正想要的,具体取决于您在处理 IP 地址时要做什么。如果您正在尝试查看流量的来源或记住用户上次连接的 IP,则某个人的私有 RFC1918 地址可能对您毫无用处,此时存储代理或 NAT 网关的公共 IP 可能更合适。
就像 X-Forwarded-For
之类的几个 HTTP 标头一样,它们可能被各种代理设置或未设置。问题在于,这些只是可以由任何人设置的HTTP标头,也没有保证它们的内容。 $_SERVER ['REMOTE_ADDR']
是 Web 服务器收到连接并将响应发送到的实际物理 IP 地址。其他任何信息都只是任意和自愿提供的信息。只有在一种情况下您才可以信任此信息:您控制设置此标头的代理。这意味着只有在您100% 确切地知道标头在哪里和如何设置时,才应该将其用于任何重要事项。
话虽如此,这里有一些示例代码:
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
编辑注: 使用上述代码会涉及安全问题。客户端可以将所有HTTP头信息(即 $_SERVER['HTTP_...
)设置为任意值。因此,使用$_SERVER ['REMOTE_ADDR']
更可靠,因为用户无法设置它。
来自:http://roshanbh.com.np/2007/12/getting-real-ip-address-in-php.html
X-Forwarded-For
或者 Client-IP
头部设置为任何想要的值。除非你有一个可信赖的反向代理,否则不应该使用这些值。 - Janos PasztorX-Forwarded-For
可能包含多个用逗号分隔的 IP 地址;应当对其进行“解析”而不是直接使用(据我所知,它几乎从未包含单个 IP)。 - Martin Tournoijecho $_SERVER['REMOTE_ADDR'];
::1
是 IPv6 中对应于 127.0.0.1
的地址。 - Camilo Martin这里是一个更简洁的代码示例,用于获取用户的IP地址。
$ip = $_SERVER['HTTP_CLIENT_IP']
? $_SERVER['HTTP_CLIENT_IP']
: ($_SERVER['HTTP_X_FORWARDED_FOR']
? $_SERVER['HTTP_X_FORWARDED_FOR']
: $_SERVER['REMOTE_ADDR']);
这里有一个使用E菲斯运算符的更短版本:
$_SERVER['HTTP_CLIENT_IP']
? : ($_SERVER['HTTP_X_FORWARDED_FOR']
? : $_SERVER['REMOTE_ADDR']);
这里是使用 isset 去除警告提示的版本(感谢 @shasi kanth):
$ip = isset($_SERVER['HTTP_CLIENT_IP'])
? $_SERVER['HTTP_CLIENT_IP']
: (isset($_SERVER['HTTP_X_FORWARDED_FOR'])
? $_SERVER['HTTP_X_FORWARDED_FOR']
: $_SERVER['REMOTE_ADDR']);
$ip = $_SERVER['HTTP_CLIENT_IP']?$_SERVER['HTTP_CLIENT_IP']:($_SERVER['HTTP_X_FORWARDED_FOR']?$_SERVER['HTTP_X_FORWARDED_FOR']:$_SERVER['REMOTE_ADDR']);
不过,非常不错。 - DavidTaubmann这应该包含在$_SERVER['REMOTE_ADDR']
变量中。
我最喜欢的解决方案是Zend Framework 2的方式。它还考虑了$_SERVER
属性HTTP_X_FORWARDED_FOR
,HTTP_CLIENT_IP
,REMOTE_ADDR
,但是它声明了一个类来设置一些可信代理并返回一个IP地址而不是一个数组。我认为这是最接近它的解决方案:
class RemoteAddress
{
/**
* Whether to use proxy addresses or not.
*
* As default this setting is disabled - IP address is mostly needed to increase
* security. HTTP_* are not reliable since can easily be spoofed. It can be enabled
* just for more flexibility, but if user uses proxy to connect to trusted services
* it's his/her own risk, only reliable field for IP address is $_SERVER['REMOTE_ADDR'].
*
* @var bool
*/
protected $useProxy = false;
/**
* List of trusted proxy IP addresses
*
* @var array
*/
protected $trustedProxies = array();
/**
* HTTP header to introspect for proxies
*
* @var string
*/
protected $proxyHeader = 'HTTP_X_FORWARDED_FOR';
// [...]
/**
* Returns client IP address.
*
* @return string IP address.
*/
public function getIpAddress()
{
$ip = $this->getIpAddressFromProxy();
if ($ip) {
return $ip;
}
// direct IP address
if (isset($_SERVER['REMOTE_ADDR'])) {
return $_SERVER['REMOTE_ADDR'];
}
return '';
}
/**
* Attempt to get the IP address for a proxied client
*
* @see http://tools.ietf.org/html/draft-ietf-appsawg-http-forwarded-10#section-5.2
* @return false|string
*/
protected function getIpAddressFromProxy()
{
if (!$this->useProxy
|| (isset($_SERVER['REMOTE_ADDR']) && !in_array($_SERVER['REMOTE_ADDR'], $this->trustedProxies))
) {
return false;
}
$header = $this->proxyHeader;
if (!isset($_SERVER[$header]) || empty($_SERVER[$header])) {
return false;
}
// Extract IPs
$ips = explode(',', $_SERVER[$header]);
// trim, so we can compare against trusted proxies properly
$ips = array_map('trim', $ips);
// remove trusted proxy IPs
$ips = array_diff($ips, $this->trustedProxies);
// Any left?
if (empty($ips)) {
return false;
}
// Since we've removed any known, trusted proxy servers, the right-most
// address represents the first IP we do not know about -- i.e., we do
// not know if it is a proxy server, or a client. As such, we treat it
// as the originating IP.
// @see http://en.wikipedia.org/wiki/X-Forwarded-For
$ip = array_pop($ips);
return $ip;
}
// [...]
}
$_SERVER
变量中获取值,并跳过一些已定义和众所周知的代理服务器。就这样。如果你认为返回的值不安全,那么请检查它或向 PHP 开发人员报告错误。 - algorhythm互联网上有不同类型的用户,因此我们希望从不同位置捕捉IP地址。这些位置包括:
1. $_SERVER['REMOTE_ADDR']
-
这里包含客户端真实的IP地址。这是您可以从用户那里找到的最可靠的值。
2. $_SERVER['REMOTE_HOST']
-
这将获取用户查看当前页面的主机名。但是,为了使此脚本工作,必须在httpd.conf内配置主机名查询。
3. $_SERVER['HTTP_CLIENT_IP']
-
这将在用户来自共享互联网服务时获取IP地址。
4. $_SERVER['HTTP_X_FORWARDED_FOR']
-
当用户位于代理后面时,这将获取来自用户的IP地址。
因此,我们可以使用以下组合函数从查看不同位置的用户那里获取真实的IP地址:
// Function to get the user IP address
function getUserIP() {
$ipaddress = '';
if (isset($_SERVER['HTTP_CLIENT_IP']))
$ipaddress = $_SERVER['HTTP_CLIENT_IP'];
else if(isset($_SERVER['HTTP_X_FORWARDED_FOR']))
$ipaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
else if(isset($_SERVER['HTTP_X_FORWARDED']))
$ipaddress = $_SERVER['HTTP_X_FORWARDED'];
else if(isset($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']))
$ipaddress = $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];
else if(isset($_SERVER['HTTP_FORWARDED_FOR']))
$ipaddress = $_SERVER['HTTP_FORWARDED_FOR'];
else if(isset($_SERVER['HTTP_FORWARDED']))
$ipaddress = $_SERVER['HTTP_FORWARDED'];
else if(isset($_SERVER['REMOTE_ADDR']))
$ipaddress = $_SERVER['REMOTE_ADDR'];
else
$ipaddress = 'UNKNOWN';
return $ipaddress;
}
function get_ip_address() {
// Check for shared Internet/ISP IP
if (!empty($_SERVER['HTTP_CLIENT_IP']) && validate_ip($_SERVER['HTTP_CLIENT_IP'])) {
return $_SERVER['HTTP_CLIENT_IP'];
}
// Check for IP addresses passing through proxies
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
// Check if multiple IP addresses exist in var
if (strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ',') !== false) {
$iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
foreach ($iplist as $ip) {
if (validate_ip($ip))
return $ip;
}
}
else {
if (validate_ip($_SERVER['HTTP_X_FORWARDED_FOR']))
return $_SERVER['HTTP_X_FORWARDED_FOR'];
}
}
if (!empty($_SERVER['HTTP_X_FORWARDED']) && validate_ip($_SERVER['HTTP_X_FORWARDED']))
return $_SERVER['HTTP_X_FORWARDED'];
if (!empty($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']) && validate_ip($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']))
return $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];
if (!empty($_SERVER['HTTP_FORWARDED_FOR']) && validate_ip($_SERVER['HTTP_FORWARDED_FOR']))
return $_SERVER['HTTP_FORWARDED_FOR'];
if (!empty($_SERVER['HTTP_FORWARDED']) && validate_ip($_SERVER['HTTP_FORWARDED']))
return $_SERVER['HTTP_FORWARDED'];
// Return unreliable IP address since all else failed
return $_SERVER['REMOTE_ADDR'];
}
/**
* Ensures an IP address is both a valid IP address and does not fall within
* a private network range.
*/
function validate_ip($ip) {
if (strtolower($ip) === 'unknown')
return false;
// Generate IPv4 network address
$ip = ip2long($ip);
// If the IP address is set and not equivalent to 255.255.255.255
if ($ip !== false && $ip !== -1) {
// Make sure to get unsigned long representation of IP address
// due to discrepancies between 32 and 64 bit OSes and
// signed numbers (ints default to signed in PHP)
$ip = sprintf('%u', $ip);
// Do private network range checking
if ($ip >= 0 && $ip <= 50331647)
return false;
if ($ip >= 167772160 && $ip <= 184549375)
return false;
if ($ip >= 2130706432 && $ip <= 2147483647)
return false;
if ($ip >= 2851995648 && $ip <= 2852061183)
return false;
if ($ip >= 2886729728 && $ip <= 2887778303)
return false;
if ($ip >= 3221225984 && $ip <= 3221226239)
return false;
if ($ip >= 3232235520 && $ip <= 3232301055)
return false;
if ($ip >= 4294967040)
return false;
}
return true;
}
快速解决方案(无错误)
function getClientIP():string
{
$keys=array('HTTP_CLIENT_IP','HTTP_X_FORWARDED_FOR','HTTP_X_FORWARDED','HTTP_FORWARDED_FOR','HTTP_FORWARDED','REMOTE_ADDR');
foreach($keys as $k)
{
if (!empty($_SERVER[$k]) && filter_var($_SERVER[$k], FILTER_VALIDATE_IP))
{
return $_SERVER[$k];
}
}
return "UNKNOWN";
}
X-FORWARDED-FOR: fakeip
HTTP 标头来欺骗 IP 地址。 - hanshenrikstrtok($k, ';,')
);2. HTTP_X_FORWARDED
不存在;3. 在这里使用 HTTP_FORWARDED
(标准化)将始终无法通过 filter_var 测试,因为它使用自己的语法(即 for=1.1.1.1;by=1.1.1.0
)。 - Jonathan Rosa