vectorface / whip
一个用于获取客户端准确IP地址信息的PHP类。
Requires
- php: >=8.0
- psr/http-message: ^1.0 || ^2.0
Requires (Dev)
- phpunit/phpunit: ^9.0
- squizlabs/php_codesniffer: ~2.0
This package is auto-updated.
Last update: 2024-08-29 19:27:35 UTC
README
Whip(代表Which Ip)是一个轻量级的PHP类,用于返回客户端的IP地址。
问题
从$_SERVER['REMOTE_ADDR']
获取客户端的IP地址似乎很简单,但这个地址并不总是准确的。例如,如果你的Web服务器后面有Varnish这样的反向代理,那么列出的IP地址将是你的代理IP,而不是客户端IP。
许多解决方案建议检查多个头部,但这些头部也可能被伪造,我们希望提供一个任何人都可部署的最终解决方案。
安装Whip。
只需运行以下composer命令
$ composer require vectorface/whip
使用Whip
将所需的use
语句添加到你的类中
use Vectorface\Whip\Whip;
要使用所有实现的方法获取IP地址,你可以这样做
$whip = new Whip(); $clientAddress = $whip->getValidIpAddress();
类将尝试每个方法来获取客户端的IP地址,从非常具体的使用案例开始,然后回退到更通用的情况。
注意,如果无法确定有效的IP地址,Whip::getValidIpAddress
方法将返回false
,因此检查错误是很重要的。
$whip = new Whip(); if (false === ($clientAddress = $whip->getValidIpAddress())) { // handle the error }
要使用特定方法获取IP地址,可以将启用方法的位掩码传递给构造函数。以下是一个使用CloudFlare的自定义HTTP头部查找IP地址的示例,如果找不到自定义头部或源IP地址不在白名单中,则回退到$_SERVER['REMOTE_ADDR']
。
$whip = new Whip(Whip::CLOUDFLARE_HEADERS | Whip::REMOTE_ADDR); $clientAddress = $whip->getValidIpAddress();
此方法有效,但问题在于如果您的网站接受来自CloudFlare之外的流量,则自定义HTTP头部很容易被伪造。为了防止这种情况,Whip允许您为每种方法指定一个接受IP地址(或地址范围)的白名单。
在可信代理后面使用Whip
一个常见的用例是在应用程序服务器前面部署一个可信的代理(nginx、varnish等)。为了正确地转发客户端IP,可信代理应配置为注入Whip可读取的头部,使用自定义头部方法。
如果可信代理配置为发送X-My-Client-IP头部,则可以使用以下方式使用Whip
$whip = new Whip( Whip::CUSTOM_HEADERS, [Whip::CUSTOM_HEADERS => [ // Whitelist your proxies. Whip::IPV4 => ['10.0.0.2', '10.0.0.3'] ]] ); $whip->addCustomHeader('HTTP_X_MY_CLIENT_IP'); $ip = $whip->getValidIpAddress();
使用CloudFlare IP范围白名单
作为一个常见的示例,当使用CloudFlare的自定义头部时,Whip可以接受CloudFlare的IP范围白名单,如果找不到自定义头部或源IP地址不在白名单中,则回退到$_SERVER['REMOTE_ADDR']
。
$whip = new Whip( Whip::CLOUDFLARE_HEADERS | Whip::REMOTE_ADDR, [ Whip::CLOUDFLARE_HEADERS => [ Whip::IPV4 => [ '199.27.128.0/21', '173.245.48.0/20', '103.21.244.0/22', '103.22.200.0/22', '103.31.4.0/22', '141.101.64.0/18', '108.162.192.0/18', '190.93.240.0/20', '188.114.96.0/20', '197.234.240.0/22', '198.41.128.0/17', '162.158.0.0/15', '104.16.0.0/12' ], Whip::IPV6 => [ '2400:cb00::/32', '2606:4700::/32', '2803:f800::/32', '2405:b500::/32', '2405:8100::/32' ] ] ] ); $clientAddress = $whip->getValidIpAddress();
请务必使用CloudFlare的实际IP范围列表,对于IPv4和IPv6。
方法列表
单个方法存储在Whip
类上的整数常量中。要组合方法,请使用位或运算符|
。当前的方法有
Whip::REMOTE_ADDR
- 使用标准的$_SERVER['REMOTE_ADDR']
。Whip::PROXY_HEADERS
- 使用以下任何一个值$_SERVER['HTTP_CLIENT_IP']
$_SERVER['HTTP_X_FORWARDED_FOR']
$_SERVER['HTTP_X_FORWARDED']
$_SERVER['HTTP_X_CLUSTER_CLIENT_IP']
$_SERVER['HTTP_FORWARDED_FOR']
$_SERVER['HTTP_FORWARDED']
$_SERVER['HTTP_X_REAL_IP']
Whip::CLOUDFLARE_HEADERS
- 使用CloudFlare提供的HTTP头“CF-Connecting-IP”。Whip::INCAPSULA_HEADERS
- 使用Incapsula提供的HTTP头“Incap-Client-IP”。Whip::CUSTOM_HEADERS
- 使用通过Whip::addCustomHeader
传入的自定义HTTP头列表。
请注意,由于代理头方法从多个可能的HTTP头中提取地址,因此它容易受到客户端欺骗。这意味着在需要信任的上下文中(如身份验证),使用代理头方法是不适当的。
使用自定义头
Whip还允许您指定要使用的自定义头。例如,您可能配置自己的代理发送一个独特的混淆头,这很难欺骗。在这个例子中,我们假设Varnish在本地运行,我们使用自定义HTTP头“X-SECRET-REAL-IP”(如果自定义头无效,则回退到$_SERVER['REMOTE_ADDR']
)。
$whip = new Whip( Whip::CUSTOM_HEADERS | Whip::REMOTE_ADDR, [ Whip::CUSTOM_HEADERS => [ Whip::IPV4 => [ '127.0.0.1' ], Whip::IPV6 => [ '::1' ] ] ] ); $whip->addCustomHeader('X-SECRET-REAL-IP'); $clientAddress = $whip->getValidIpAddress();
有效的IP范围
对于IPv4,Whip接受三种类型的IP范围
- 星号通配符(192.168.*)
- 破折号范围(192.168.0.0-192.168.255.255)
- CIDR掩码表示法(192.168.0.0/16)
对于IPv6,Whip仅接受CIDR掩码表示法(fc00::/7)。
此外,您还可以指定一组精确的IP地址而不是一组范围。
IP范围过滤
Whip还可以用于提供简单的IP范围匹配。例如,
$range = new Vectorface\Whip\IpRange\Ipv4Range('10.0.*'); if ($range->containsIp($ipv4Address)) { // handle the IP address being within the range } $range = new Vectorface\Whip\IpRange\Ipv6Range('::1/32'); if ($range->containsIp($ipv6Address)) { // handle the IP address being within the range }
PSR-7请求和其他请求
Whip支持使用PSR-7 (http-message)请求实例代替$_SERVER
全局变量。例如,
// Get a Psr\Http\Message\ServerRequestInterface implementation from somewhere. $request = ServerRequestFactory::fromGlobals(); // You can pass the request in the constructor. $whip = new Whip(Whip::REMOTE_ADDR, [], $request); // ... or set the request as the source of data. $whip->setSource($request); // ... or pass it to any function accepting a source argument. $ip = $whip->getValidIpAddress($request);
可以通过实现RequestAdapter(src/Request/RequestAdapter)来支持其他请求格式。