j0k3r/httplug-ssrf-plugin

HTTPlug 的服务器端请求伪造(SSRF)防护插件

v3.0.0 2023-04-18 06:11 UTC

This package is auto-updated.

Last update: 2024-09-18 09:06:36 UTC


README

CI Coverage Status

SafeCurl启发,该插件旨在验证URL的每一部分与白名单或黑名单,以帮助在HTTPlug使用时防止服务器端请求伪造攻击。

URL的每一部分都会被拆分并验证,包括将域名解析为其IP地址。

安装

可以使用Composer将其包含在任何PHP项目中。

composer require j0k3r/httplug-ssrf-plugin

使用方法

use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\ServerSideRequestForgeryProtectionPlugin;
use Http\Client\Common\PluginClient;
use Http\Discovery\Psr18ClientDiscovery;

$ssrfPlugin = new ServerSideRequestForgeryProtectionPlugin();

$pluginClient = new PluginClient(
    Psr18ClientDiscovery::find(),
    [$ssrfPlugin]
);

如果URL无效,插件会抛出Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Exception\InvalidURLException异常。

选项

默认选项是不允许访问任何私有IP地址,并且只允许HTTP(S)连接。

如果您想添加自己的选项(例如,将任何请求到您控制的域的黑名单),只需获取一个新的Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Options对象,将其添加到白名单或黑名单中,并与方法调用一起传递。

域使用正则表达式语法表示,而IP、方案和端口使用标准字符串(IP地址可以用CIDR表示法指定)。

use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Options;
use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\ServerSideRequestForgeryProtectionPlugin;
use Http\Discovery\Psr17FactoryDiscovery;
use Http\Discovery\Psr18ClientDiscovery;
use Http\Client\Common\PluginClient;

$options = new Options();
$options->addToList(Options::LIST_BLACKLIST, Options::TYPE_DOMAIN, '(.*)\.example\.com');

$pluginClient = new PluginClient(
    Psr18ClientDiscovery::find(),
    [new ServerSideRequestForgeryProtectionPlugin($options)]
);

// This will throw an Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Exception\InvalidURLException\InvalidDomainException
$request = Psr17FactoryDiscovery::findRequestFactory()->createRequest('GET', 'https://www.example.com');
$response = $pluginClient->sendRequest($request);

$options = new Options();
$options->setList(Options::LIST_WHITELIST, [Options::TYPE_SCHEME => ['https']]);

$pluginClient = new PluginClient(
    Psr18ClientDiscovery::find(),
    [new ServerSideRequestForgeryProtectionPlugin($options)]
);

// This will be allowed, and return the response
$request = Psr17FactoryDiscovery::findRequestFactory()->createRequest('GET', 'https://www.example.com');
$response = $pluginClient->sendRequest($request);

// This will throw an Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Exception\InvalidURLException\InvalidDomainException
$request = Psr17FactoryDiscovery::findRequestFactory()->createRequest('GET', 'https://www.example.com');
$response = $pluginClient->sendRequest($request);

可选保护

除了标准检查外,还有两个可选的保护。

第一个是防止DNS重绑定攻击。这可以通过在Options对象上调用enablePinDns方法来启用。这个问题的一个主要问题是SSL证书无法验证。这是由于在Host头中发送了实际的主机名,并且URL使用了IP地址。

$options = new Options();
$options->enablePinDns();

第二个是禁用URL中的凭据使用,因为PHP的parse_url返回的值与cURL使用的值不同。这是一个临时修复。

use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Options;
use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\ServerSideRequestForgeryProtectionPlugin;
use Http\Discovery\Psr17FactoryDiscovery;
use Http\Discovery\Psr18ClientDiscovery;
use Http\Client\Common\PluginClient;

$options = new Options();
$options->disableSendCredentials();

//This will throw an Http\Client\Exception\RequestException
$pluginClient = new PluginClient(
    Psr18ClientDiscovery::find(),
    [new ServerSideRequestForgeryProtectionPlugin($options)]
);
$request = Psr17FactoryDiscovery::findRequestFactory()->createRequest('GET', 'https://user:[email protected]');
$response = $pluginClient->sendRequest($request);

注意事项

由于该库使用gethostbynamel来解析域名,它不兼容IPv6,因此该类目前只能与IPv4一起使用。