src
11 months ago
.scrutinizer.yml
1 year ago
LICENSE
1 year ago
README.md
11 months ago
composer.json
1 year ago
README.md
242 lines
| 1 | # Whip |
| 2 | |
| 3 | ## Notice! |
| 4 | |
| 5 | > The original version was modified by AAM team to be compatible with PHP version lower than 7.0.4. |
| 6 | |
| 7 | [](https://travis-ci.org/Vectorface/whip](https://travis-ci.org/Vectorface/whip](https://travis-ci.org/Vectorface/whip) |
| 8 | [](https://scrutinizer-ci.com/g/Vectorface/whip/?branch=master](https://scrutinizer-ci.com/g/Vectorface/whip/?branch=master](https://scrutinizer-ci.com/g/Vectorface/whip/?branch=master) |
| 9 | [](https://scrutinizer-ci.com/g/Vectorface/whip/?branch=master](https://scrutinizer-ci.com/g/Vectorface/whip/?branch=master](https://scrutinizer-ci.com/g/Vectorface/whip/?branch=master) |
| 10 | [](https://packagist.org/packages/vectorface/whip](https://packagist.org/packages/vectorface/whip](https://packagist.org/packages/vectorface/whip) |
| 11 | [](https://packagist.org/packages/vectorface/whip](https://packagist.org/packages/vectorface/whip](https://packagist.org/packages/vectorface/whip) |
| 12 | |
| 13 | Whip (stands for Which Ip) is a lightweight class for returning a client's IP address in PHP. |
| 14 | |
| 15 | ## The Problem |
| 16 | |
| 17 | It may seem trivial to simply pull the client's IP address from |
| 18 | `$_SERVER['REMOTE_ADDR']` but this address is not always accurate. For example, |
| 19 | if your web servers are behind a reverse proxy like Varnish, the IP address |
| 20 | listed will be that of your proxy and not the client. |
| 21 | |
| 22 | Many solutions propose checking multiple headers but those headers can be |
| 23 | spoofed as well and we want to present a final solution anyone can deploy. |
| 24 | |
| 25 | ## Installing Whip. |
| 26 | |
| 27 | Simply run the following [](https://getcomposer.org/composer](https://getcomposer.org/](https://getcomposer.org/) command: |
| 28 | |
| 29 | ```shell |
| 30 | $ composer require vectorface/whip |
| 31 | ``` |
| 32 | |
| 33 | ## Using Whip |
| 34 | |
| 35 | Add the required `use` statement to your class |
| 36 | |
| 37 | ```php |
| 38 | use Vectorface\Whip\Whip; |
| 39 | ``` |
| 40 | |
| 41 | To fetch an IP address using every implemented method, you can simply do |
| 42 | |
| 43 | ```php |
| 44 | $whip = new Whip(); |
| 45 | $clientAddress = $whip->getValidIpAddress(); |
| 46 | ``` |
| 47 | |
| 48 | The class will attempt every method to retrieve the client's IP address |
| 49 | starting with very specific use cases and falling back to more general use |
| 50 | cases. |
| 51 | |
| 52 | Note, that the method `Whip::getValidIpAddress` will return `false` if no |
| 53 | valid IP address could be determined, so it is important to check for errors. |
| 54 | |
| 55 | ```php |
| 56 | $whip = new Whip(); |
| 57 | if (false === ($clientAddress = $whip->getValidIpAddress())) { |
| 58 | // handle the error |
| 59 | } |
| 60 | ``` |
| 61 | |
| 62 | To fetch an IP address using a specific method, you can pass a bitmask of |
| 63 | enabled methods to the constructor. Here is an example of looking up the IP |
| 64 | address using CloudFlare's custom HTTP header, and falling back to |
| 65 | `$_SERVER['REMOTE_ADDR']` otherwise. |
| 66 | |
| 67 | ```php |
| 68 | $whip = new Whip(Whip::CLOUDFLARE_HEADERS | Whip::REMOTE_ADDR); |
| 69 | $clientAddress = $whip->getValidIpAddress(); |
| 70 | ``` |
| 71 | |
| 72 | This method works, but there is the problem that the custom HTTP header can |
| 73 | easily be spoofed if your sites accept traffic not from CloudFlare. To prevent |
| 74 | this, Whip allows you to specify a whitelist of IP addresses (or address ranges) |
| 75 | that you accept per method. |
| 76 | |
| 77 | ## Using Whip Behind a Trusted Proxy |
| 78 | |
| 79 | A common use case is to deploy a trusted proxy (nginx, varnish, and many others) |
| 80 | in front of an application server. To forward the correct client IP, the trusted |
| 81 | proxy should be configured to inject a header for Whip to read with the custom |
| 82 | headers method. |
| 83 | |
| 84 | If the trusted proxy is configured to send a X-My-Client-IP header, Whip |
| 85 | could be used as follows: |
| 86 | |
| 87 | ```php |
| 88 | $whip = new Whip( |
| 89 | Whip::CUSTOM_HEADERS, |
| 90 | [Whip::CUSTOM_HEADERS => [ // Whitelist your proxies. |
| 91 | Whip::IPV4 => ['10.0.0.2', '10.0.0.3'] |
| 92 | ]] |
| 93 | ); |
| 94 | $whip->addCustomHeader('HTTP_X_MY_CLIENT_IP'); |
| 95 | $ip = $whip->getValidIpAddress(); |
| 96 | ``` |
| 97 | |
| 98 | ## Using the CloudFlare IP Range Whitelist |
| 99 | |
| 100 | As a common example, Whip can accept a whitelist of IP ranges for CloudFlare |
| 101 | when using their custom header and fall back to `$_SERVER['REMOTE_ADDR']` if the |
| 102 | custom header was not found or if the source IP address does match any in the |
| 103 | whitelist. |
| 104 | |
| 105 | ```php |
| 106 | $whip = new Whip( |
| 107 | Whip::CLOUDFLARE_HEADERS | Whip::REMOTE_ADDR, |
| 108 | [ |
| 109 | Whip::CLOUDFLARE_HEADERS => [ |
| 110 | Whip::IPV4 => [ |
| 111 | '199.27.128.0/21', |
| 112 | '173.245.48.0/20', |
| 113 | '103.21.244.0/22', |
| 114 | '103.22.200.0/22', |
| 115 | '103.31.4.0/22', |
| 116 | '141.101.64.0/18', |
| 117 | '108.162.192.0/18', |
| 118 | '190.93.240.0/20', |
| 119 | '188.114.96.0/20', |
| 120 | '197.234.240.0/22', |
| 121 | '198.41.128.0/17', |
| 122 | '162.158.0.0/15', |
| 123 | '104.16.0.0/12' |
| 124 | ], |
| 125 | Whip::IPV6 => [ |
| 126 | '2400:cb00::/32', |
| 127 | '2606:4700::/32', |
| 128 | '2803:f800::/32', |
| 129 | '2405:b500::/32', |
| 130 | '2405:8100::/32' |
| 131 | ] |
| 132 | ] |
| 133 | ] |
| 134 | ); |
| 135 | $clientAddress = $whip->getValidIpAddress(); |
| 136 | ``` |
| 137 | |
| 138 | Please be sure to use the actual list of IP ranges from CloudFlare for |
| 139 | [](https://www.cloudflare.com/ips-v4IPv4](https://www.cloudflare.com/ips-v4](https://www.cloudflare.com/ips-v4) and |
| 140 | [](https://www.cloudflare.com/ips-v6IPv6](https://www.cloudflare.com/ips-v6](https://www.cloudflare.com/ips-v6). |
| 141 | |
| 142 | ## List of Methods |
| 143 | |
| 144 | The individual methods are stored as integer constants on the `Whip` class. |
| 145 | To combine methods, use the bitwise OR operator `|`. The current methods are: |
| 146 | |
| 147 | - `Whip::REMOTE_ADDR` - Uses the standard `$_SERVER['REMOTE_ADDR']`. |
| 148 | - `Whip::PROXY_HEADERS` - Uses any of the following values: |
| 149 | - `$_SERVER['HTTP_CLIENT_IP']` |
| 150 | - `$_SERVER['HTTP_X_FORWARDED_FOR']` |
| 151 | - `$_SERVER['HTTP_X_FORWARDED']` |
| 152 | - `$_SERVER['HTTP_X_CLUSTER_CLIENT_IP']` |
| 153 | - `$_SERVER['HTTP_FORWARDED_FOR']` |
| 154 | - `$_SERVER['HTTP_FORWARDED']` |
| 155 | - `$_SERVER['HTTP_X_REAL_IP']` |
| 156 | - `Whip::CLOUDFLARE_HEADERS` - Uses the CloudFlare provided HTTP header |
| 157 | "CF-Connecting-IP". |
| 158 | - `Whip::INCAPSULA_HEADERS` - Use the Incapsula provided HTTP header |
| 159 | "Incap-Client-IP". |
| 160 | - `Whip::CUSTOM_HEADERS` - Uses a custom list of HTTP headers passed into |
| 161 | `Whip::addCustomHeader`. |
| 162 | |
| 163 | Please note that the proxy headers method can be susceptible to client spoofing |
| 164 | because it extracts addresses from several possible HTTP headers. This means |
| 165 | that using the proxy headers method is not appropriate where trust is required, |
| 166 | like in the context of authentication. |
| 167 | |
| 168 | ## Using a Custom Header |
| 169 | |
| 170 | Whip can also allow you to specify a custom header to use. For example, you may |
| 171 | configure your own proxy to send a unique obfuscated header internally that |
| 172 | would be hard to spoof. In this example, we assume Varnish is run locally and |
| 173 | we use a custom HTTP header "X-SECRET-REAL-IP" (and fall back to |
| 174 | `$_SERVER['REMOTE_ADDR']` if the custom header doesn't work). |
| 175 | |
| 176 | ```php |
| 177 | $whip = new Whip( |
| 178 | Whip::CUSTOM_HEADERS | Whip::REMOTE_ADDR, |
| 179 | [ |
| 180 | Whip::CUSTOM_HEADERS => [ |
| 181 | Whip::IPV4 => [ |
| 182 | '127.0.0.1' |
| 183 | ], |
| 184 | Whip::IPV6 => [ |
| 185 | '::1' |
| 186 | ] |
| 187 | ] |
| 188 | ] |
| 189 | ); |
| 190 | $whip->addCustomHeader('X-SECRET-REAL-IP'); |
| 191 | $clientAddress = $whip->getValidIpAddress(); |
| 192 | ``` |
| 193 | |
| 194 | ## Valid IP Ranges |
| 195 | |
| 196 | For IPv4, Whip accepts three types of IP ranges: |
| 197 | |
| 198 | - Asterisk wildcard (192.168.\*) |
| 199 | - Dashed range (192.168.0.0-192.168.255.255) |
| 200 | - CIDR bitmask notation (192.168.0.0/16) |
| 201 | |
| 202 | For IPv6, Whip only accepts the CIDR bitmask notation (fc00::/7). |
| 203 | |
| 204 | Furthermore, you can specify a list of exact IP addresses instead of a list of |
| 205 | ranges. |
| 206 | |
| 207 | ## IP Range Filtering |
| 208 | |
| 209 | Whip can also be used to provide simple IP range matching. For example, |
| 210 | |
| 211 | ```php |
| 212 | $range = new Vectorface\Whip\IpRange\Ipv4Range('10.0.*'); |
| 213 | if ($range->containsIp($ipv4Address)) { |
| 214 | // handle the IP address being within the range |
| 215 | } |
| 216 | |
| 217 | $range = new Vectorface\Whip\IpRange\Ipv6Range('::1/32'); |
| 218 | if ($range->containsIp($ipv6Address)) { |
| 219 | // handle the IP address being within the range |
| 220 | } |
| 221 | ``` |
| 222 | |
| 223 | ## PSR-7 Requests, and Others |
| 224 | |
| 225 | Whip supports using [](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-7-http-message.mdPSR-7 (http-message)](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-7-http-message.md](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-7-http-message.md) request instances in place of the `$_SERVER` superglobal. For example, |
| 226 | |
| 227 | ```php |
| 228 | // Get a Psr\Http\Message\ServerRequestInterface implementation from somewhere. |
| 229 | $request = ServerRequestFactory::fromGlobals(); |
| 230 | |
| 231 | // You can pass the request in the constructor. |
| 232 | $whip = new Whip(Whip::REMOTE_ADDR, [], $request); |
| 233 | |
| 234 | // ... or set the request as the source of data. |
| 235 | $whip->setSource($request); |
| 236 | |
| 237 | // ... or pass it to any function accepting a source argument. |
| 238 | $ip = $whip->getValidIpAddress($request); |
| 239 | ``` |
| 240 | |
| 241 | Other request formats can be supported via a RequestAdapter (src/Request/RequestAdapter) implementation. |
| 242 |