[转]深入分析几种PHP获取客户端IP的情况

本文详细介绍了在PHP中获取客户端IP时的多种情况,包括直接IP、代理服务器IP以及如何通过HTTP_X_FORWARDED_FOR获取真实客户端IP。文章提供了不同代理服务器类型下的IP获取情况说明,并附带了PHP代码实现。此外,还提到了在使用不同环境如IIS时获取IP的方法。最后,提供了四种获取客户端IP的常用方法供开发者参考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这篇文章中,我们将会为大家详细介绍PHP获取客户端IP的几种情况分析。我们在使用PHP获取的IP可能是客户端真实的IP,也可能是代理服务器的IP,也有可能根本取不到任何IP值。

    在PHP获取客户端IP中常使用 $_SERVER["REMOTE_ADDR"] 。但如果客户端是使用代理服务器来访问,那取到的是代理服务器的 IP 地址,而不是真正的客户端 IP 地址。要想透过代理服务器取得客户端的真实 IP 地址,就要使用 $_SERVER["HTTP_X_FORWARDED_FOR"] 来读取。

    但只有客户端使用“透明代理”的情况下,$_SERVER["HTTP_X_FORWARDED_FOR"] 的值才是客户端真正的IP(如果是多层代理,该值可能是由客户端真正IP和多个代理服务器的IP组成,由逗号“,”分隔),而在“匿名代理”、“欺骗性代理”的情况下是代理服务器的IP值(如果是多层代理,该值可能由多个代理服务器的IP组成,由逗号“,”分隔),在“高匿名代理”的情况下是空值。

    关于HTTP头信息中的REMOTE_ADDR、HTTP_FORWARDED_FOR值,我们在下文中有详细的介绍,假设客户端真实IP是221.5.252.160:

    一、没有使用代理服务器的PHP获取客户端IP情况:

    REMOTE_ADDR = 客户端IP
    HTTP_X_FORWARDED_FOR = 没数值或不显示

    二、使用透明代理服务器的情况:Transparent Proxies

    REMOTE_ADDR = 最后一个代理服务器 IP
    HTTP_X_FORWARDED_FOR = 客户端真实 IP (经过多个代理服务器时,这个值类似:221.5.252.160, 203.98.182.163, 203.129.72.215)
    这类代理服务器还是将客户端真实的IP发送给了访问对象,无法达到隐藏真实身份的目的。

    三、使用普通匿名代理服务器的PHP获取客户端IP情况:Anonymous Proxies

    REMOTE_ADDR = 最后一个代理服务器 IP
    HTTP_X_FORWARDED_FOR = 代理服务器 IP (经过多个代理服务器时,这个值类似:203.98.182.163, 203.98.182.163, 203.129.72.215)
    这种情况下隐藏了客户端的真实IP,但是向访问对象透露了客户端是使用代理服务器访问它们的。

    四、使用欺骗性代理服务器的情况:Distorting Proxies

    REMOTE_ADDR = 代理服务器 IP
    HTTP_X_FORWARDED_FOR = 随机的 IP(经过多个代理服务器时,这个值类似:220.4.251.159, 203.98.182.163, 203.129.72.215)
    这种情况下同样透露了客户端是使用了代理服务器,但编造了一个虚假的随机IP(220.4.251.159)代替客户端的真实IP来欺骗它。

    五、使用高匿名代理服务器的PHP获取客户端IP情况:High Anonymity Proxies (Elite proxies)

    REMOTE_ADDR = 代理服务器 IP
    HTTP_X_FORWARDED_FOR = 没数值或不显示

    无论是REMOTE_ADDR还是HTTP_FORWARDED_FOR,这些头消息未必能够取得到,因为不同的浏览器不同的网络设备可能发送不同的IP头消息。因此PHP使用$_SERVER["REMOTE_ADDR"] 、$_SERVER["HTTP_X_FORWARDED_FOR"] 获取的值可能是空值也可能是“unknown”值。

    因此,使用PHP获取客户端IP的代码可以如下:

     
    1. function getip() {  
    2. $unknown = 'unknown';  
    3. if ( isset($_SERVER['HTTP_X_FORWARDED_FOR']) 
      && $_SERVER['HTTP_X_FORWARDED_FOR'] 
      && strcasecmp($_SERVER['HTTP_X_FORWARDED_FOR'], 
      $unknown) ) {  
    4. $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];  
    5. } elseif ( isset($_SERVER['REMOTE_ADDR']) 
      && $_SERVER['REMOTE_ADDR'] && 
      strcasecmp($_SERVER['REMOTE_ADDR'], $unknown) ) {  
    6. $ip = $_SERVER['REMOTE_ADDR'];  
    7. }  
    8. /*  
    9. 处理多层代理的情况  
    10. 或者使用正则方式:$ip = preg_match("/[\d\.]
      {7,15}/", $ip, $matches) ? $matches[0] : $unknown;  
    11. */  
    12. if (false !== strpos($ip, ','))  
    13. $ip = reset(explode(',', $ip));  
    14.  return $ip;  


    PHP获取客户端IP时另外一点需注意,使用函数getenv(’HTTP_X_FORWARDED_FOR’)或getenv(’REMOTE_ADDR’) 也可以如上代码一样取得同样的效果。但getenv()不支持在IIS的isapi方式下运行的PHP。

    原文地址:http://developer.51cto.com/art/200912/166495.htm

    常见的三种方法:

    第一种方法,还算靠谱,本人以前一直用的是这个方法:

    function get_real_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(!eregi ('^(10│172.16│192.168).', $ips[$i])){
    				$ip=$ips[$i];
    				break;
    			}
    		}
    	}
    	return ($ip ? $ip : $_SERVER['REMOTE_ADDR']); 
    }
    第二种方法:

    function get_real_ip(){
        static $realip;
        if(isset($_SERVER)){
            if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
                $realip=$_SERVER['HTTP_X_FORWARDED_FOR'];
            }else if(isset($_SERVER['HTTP_CLIENT_IP'])){
                $realip=$_SERVER['HTTP_CLIENT_IP'];
            }else{
                $realip=$_SERVER['REMOTE_ADDR'];
            }
        }else{
            if(getenv('HTTP_X_FORWARDED_FOR')){
                $realip=getenv('HTTP_X_FORWARDED_FOR');
            }else if(getenv('HTTP_CLIENT_IP')){
                $realip=getenv('HTTP_CLIENT_IP');
            }else{
                $realip=getenv('REMOTE_ADDR');
            }
        }
        return $realip;
    }

    第三种方法,摘自DISCUZ,应该还不错吧!

    // 获取IP地址(摘自discuz)
    function getIp(){
    	$ip='未知IP';
    	if(!empty($_SERVER['HTTP_CLIENT_IP'])){
    		return is_ip($_SERVER['HTTP_CLIENT_IP'])?$_SERVER['HTTP_CLIENT_IP']:$ip;
    	}elseif(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])){
    		return is_ip($_SERVER['HTTP_X_FORWARDED_FOR'])?$_SERVER['HTTP_X_FORWARDED_FOR']:$ip;
    	}else{
    		return is_ip($_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR']:$ip;
    	}
    }
    function is_ip($str){
    	$ip=explode('.',$str);
    	for($i=0;$i<count($ip);$i++){  
    		if($ip[$i]>255){  
    			return false;  
    		}  
    	}  
    	return preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/',$str);  
    }

    原文地址:http://www.phpernote.com/php-function/322.html

    第四种方法,摘自codeigniter:

    /**
    	 * Fetch the IP Address
    	 *
    	 * Determines and validates the visitor's IP address.
    	 *
    	 * @return	string	IP address
    	 */
    	public function ip_address()
    	{
    		if ($this->ip_address !== FALSE)
    		{
    			return $this->ip_address;
    		}
    
    		$proxy_ips = config_item('proxy_ips');
    		if ( ! empty($proxy_ips) && ! is_array($proxy_ips))
    		{
    			$proxy_ips = explode(',', str_replace(' ', '', $proxy_ips));
    		}
    
    		$this->ip_address = $this->server('REMOTE_ADDR');
    
    		if ($proxy_ips)
    		{
    			foreach (array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP') as $header)
    			{
    				if (($spoof = $this->server($header)) !== NULL)
    				{
    					// Some proxies typically list the whole chain of IP
    					// addresses through which the client has reached us.
    					// e.g. client_ip, proxy_ip1, proxy_ip2, etc.
    					sscanf($spoof, '%[^,]', $spoof);
    
    					if ( ! $this->valid_ip($spoof))
    					{
    						$spoof = NULL;
    					}
    					else
    					{
    						break;
    					}
    				}
    			}
    
    			if ($spoof)
    			{
    				for ($i = 0, $c = count($proxy_ips); $i < $c; $i++)
    				{
    					// Check if we have an IP address or a subnet
    					if (strpos($proxy_ips[$i], '/') === FALSE)
    					{
    						// An IP address (and not a subnet) is specified.
    						// We can compare right away.
    						if ($proxy_ips[$i] === $this->ip_address)
    						{
    							$this->ip_address = $spoof;
    							break;
    						}
    
    						continue;
    					}
    
    					// We have a subnet ... now the heavy lifting begins
    					isset($separator) OR $separator = $this->valid_ip($this->ip_address, 'ipv6') ? ':' : '.';
    
    					// If the proxy entry doesn't match the IP protocol - skip it
    					if (strpos($proxy_ips[$i], $separator) === FALSE)
    					{
    						continue;
    					}
    
    					// Convert the REMOTE_ADDR IP address to binary, if needed
    					if ( ! isset($ip, $sprintf))
    					{
    						if ($separator === ':')
    						{
    							// Make sure we're have the "full" IPv6 format
    							$ip = explode(':',
    								str_replace('::',
    									str_repeat(':', 9 - substr_count($this->ip_address, ':')),
    									$this->ip_address
    								)
    							);
    
    							for ($j = 0; $j < 8; $j++)
    							{
    								$ip[$j] = intval($ip[$j], 16);
    							}
    
    							$sprintf = '%016b%016b%016b%016b%016b%016b%016b%016b';
    						}
    						else
    						{
    							$ip = explode('.', $this->ip_address);
    							$sprintf = '%08b%08b%08b%08b';
    						}
    
    						$ip = vsprintf($sprintf, $ip);
    					}
    
    					// Split the netmask length off the network address
    					sscanf($proxy_ips[$i], '%[^/]/%d', $netaddr, $masklen);
    
    					// Again, an IPv6 address is most likely in a compressed form
    					if ($separator === ':')
    					{
    						$netaddr = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($netaddr, ':')), $netaddr));
    						for ($i = 0; $i < 8; $i++)
    						{
    							$netaddr[$i] = intval($netaddr[$i], 16);
    						}
    					}
    					else
    					{
    						$netaddr = explode('.', $netaddr);
    					}
    
    					// Convert to binary and finally compare
    					if (strncmp($ip, vsprintf($sprintf, $netaddr), $masklen) === 0)
    					{
    						$this->ip_address = $spoof;
    						break;
    					}
    				}
    			}
    		}
    
    		if ( ! $this->valid_ip($this->ip_address))
    		{
    			return $this->ip_address = '0.0.0.0';
    		}
    
    		return $this->ip_address;
    	}
    
    	// --------------------------------------------------------------------
    
    	/**
    	 * Validate IP Address
    	 *
    	 * @param	string	$ip	IP address
    	 * @param	string	$which	IP protocol: 'ipv4' or 'ipv6'
    	 * @return	bool
    	 */
    	public function valid_ip($ip, $which = '')
    	{
    		switch (strtolower($which))
    		{
    			case 'ipv4':
    				$which = FILTER_FLAG_IPV4;
    				break;
    			case 'ipv6':
    				$which = FILTER_FLAG_IPV6;
    				break;
    			default:
    				$which = NULL;
    				break;
    		}
    
    		return (bool) filter_var($ip, FILTER_VALIDATE_IP, $which);
    	}



    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值