一个用于获取客户端准确IP地址信息的PHP类。

v0.5.0 2024-01-22 01:03 UTC

This package is auto-updated.

Last update: 2024-08-29 19:27:35 UTC


README

Build Status Code Coverage Scrutinizer Code Quality Latest Stable Version License

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范围列表,对于IPv4IPv6

方法列表

单个方法存储在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)来支持其他请求格式。