1<?php 2/** 3 * Zend Framework (http://framework.zend.com/) 4 * 5 * @link http://github.com/zendframework/zf2 for the canonical source repository 6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 7 * @license http://framework.zend.com/license/new-bsd New BSD License 8 */ 9 10namespace Zend\Http\PhpEnvironment; 11 12/** 13 * Functionality for determining client IP address. 14 */ 15class RemoteAddress 16{ 17 /** 18 * Whether to use proxy addresses or not. 19 * 20 * As default this setting is disabled - IP address is mostly needed to increase 21 * security. HTTP_* are not reliable since can easily be spoofed. It can be enabled 22 * just for more flexibility, but if user uses proxy to connect to trusted services 23 * it's his/her own risk, only reliable field for IP address is $_SERVER['REMOTE_ADDR']. 24 * 25 * @var bool 26 */ 27 protected $useProxy = false; 28 29 /** 30 * List of trusted proxy IP addresses 31 * 32 * @var array 33 */ 34 protected $trustedProxies = array(); 35 36 /** 37 * HTTP header to introspect for proxies 38 * 39 * @var string 40 */ 41 protected $proxyHeader = 'HTTP_X_FORWARDED_FOR'; 42 43 44 /** 45 * Changes proxy handling setting. 46 * 47 * This must be static method, since validators are recovered automatically 48 * at session read, so this is the only way to switch setting. 49 * 50 * @param bool $useProxy Whether to check also proxied IP addresses. 51 * @return RemoteAddress 52 */ 53 public function setUseProxy($useProxy = true) 54 { 55 $this->useProxy = $useProxy; 56 return $this; 57 } 58 59 /** 60 * Checks proxy handling setting. 61 * 62 * @return bool Current setting value. 63 */ 64 public function getUseProxy() 65 { 66 return $this->useProxy; 67 } 68 69 /** 70 * Set list of trusted proxy addresses 71 * 72 * @param array $trustedProxies 73 * @return RemoteAddress 74 */ 75 public function setTrustedProxies(array $trustedProxies) 76 { 77 $this->trustedProxies = $trustedProxies; 78 return $this; 79 } 80 81 /** 82 * Set the header to introspect for proxy IPs 83 * 84 * @param string $header 85 * @return RemoteAddress 86 */ 87 public function setProxyHeader($header = 'X-Forwarded-For') 88 { 89 $this->proxyHeader = $this->normalizeProxyHeader($header); 90 return $this; 91 } 92 93 /** 94 * Returns client IP address. 95 * 96 * @return string IP address. 97 */ 98 public function getIpAddress() 99 { 100 $ip = $this->getIpAddressFromProxy(); 101 if ($ip) { 102 return $ip; 103 } 104 105 // direct IP address 106 if (isset($_SERVER['REMOTE_ADDR'])) { 107 return $_SERVER['REMOTE_ADDR']; 108 } 109 110 return ''; 111 } 112 113 /** 114 * Attempt to get the IP address for a proxied client 115 * 116 * @see http://tools.ietf.org/html/draft-ietf-appsawg-http-forwarded-10#section-5.2 117 * @return false|string 118 */ 119 protected function getIpAddressFromProxy() 120 { 121 if (!$this->useProxy 122 || (isset($_SERVER['REMOTE_ADDR']) && !in_array($_SERVER['REMOTE_ADDR'], $this->trustedProxies)) 123 ) { 124 return false; 125 } 126 127 $header = $this->proxyHeader; 128 if (!isset($_SERVER[$header]) || empty($_SERVER[$header])) { 129 return false; 130 } 131 132 // Extract IPs 133 $ips = explode(',', $_SERVER[$header]); 134 // trim, so we can compare against trusted proxies properly 135 $ips = array_map('trim', $ips); 136 // remove trusted proxy IPs 137 $ips = array_diff($ips, $this->trustedProxies); 138 139 // Any left? 140 if (empty($ips)) { 141 return false; 142 } 143 144 // Since we've removed any known, trusted proxy servers, the right-most 145 // address represents the first IP we do not know about -- i.e., we do 146 // not know if it is a proxy server, or a client. As such, we treat it 147 // as the originating IP. 148 // @see http://en.wikipedia.org/wiki/X-Forwarded-For 149 $ip = array_pop($ips); 150 return $ip; 151 } 152 153 /** 154 * Normalize a header string 155 * 156 * Normalizes a header string to a format that is compatible with 157 * $_SERVER 158 * 159 * @param string $header 160 * @return string 161 */ 162 protected function normalizeProxyHeader($header) 163 { 164 $header = strtoupper($header); 165 $header = str_replace('-', '_', $header); 166 if (0 !== strpos($header, 'HTTP_')) { 167 $header = 'HTTP_' . $header; 168 } 169 return $header; 170 } 171} 172