vinodsai-a / safecurl
用于替换'curl_exec'的简单替代方案,旨在防止SSRF攻击。
Requires
- php: ~7.0
- ext-curl: *
Requires (Dev)
- friendsofphp/php-cs-fixer: ~2.0
- symfony/phpunit-bridge: ^5.0
README
SafeCurl旨在作为PHP中curl_exec函数的简单替代方案。SafeCurl会对URL的每一部分与白名单或黑名单进行验证,以帮助防止服务器端请求伪造攻击。
有关项目的更多信息,请参阅博客文章'SafeCurl: SSRF Protection, and a "Capture the Bitcoins"'。
保护措施
将URL的每一部分分解并验证,包括将域名解析为其IP地址。
如果您选择启用"FOLLOWLOCATION",则任何重定向都会被捕获并重新验证。
安装
SafeCurl可以通过使用Composer包含在任何PHP项目中。在composer.json
文件的require
部分包含以下内容。
"require": {
"vinodsai-a\safecurl": "~3.0"
}
然后更新Composer。
composer update
用法
只需替换curl_exec
并在其中添加try {} catch {}
块即可。
use fin1te\SafeCurl\SafeCurl; use fin1te\SafeCurl\Exception; try { $url = 'http://www.google.com'; $curlHandle = curl_init(); //Your usual cURL options curl_setopt($curlHandle, CURLOPT_USERAGENT, 'Mozilla/5.0 (SafeCurl)'); //Execute using SafeCurl $safeCurl = new SafeCurl($curlHandle); $response = $safeCurl->execute($url); } catch (Exception $e) { //URL wasn't safe }
选项
默认选项为不允许访问任何私有IP地址,并且只允许HTTP(S)连接。
如果您希望添加自己的选项(例如,将任何请求到您控制的域名的请求加入黑名单),只需创建一个新的SimpleCurl\Options对象,将其添加到白名单或黑名单中,并将其与方法调用一起传递。
域名使用正则表达式语法表示,而IP、协议和端口使用标准字符串(IP可以使用CIDR表示法指定)。
use fin1te\SafeCurl\SafeCurl; use fin1te\SafeCurl\Options; $options = new Options(); $options->addToList('blacklist', 'domain', '(.*)\.fin1te\.net'); $options->addToList('whitelist', 'scheme', 'ftp'); $curlHandle = curl_init(); //This will now throw an InvalidDomainException $safeCurl = new SafeCurl($curlHandle, $options); $response = $safeCurl->execute('http://safecurl.fin1te.net'); //Whilst this will be allowed, and return the response $safeCurl = new SafeCurl($curlHandle, $options); $response = $safeCurl->execute('ftp://fin1te.net');
由于我们无法访问已设置的任何cURL选项(请参阅注意事项部分),要启用CURL_FOLLOWREDIRECTS
,必须调用enableFollowRedirects()
方法。如果您想指定重定向限制,需要调用setMaxRedirects()
。传递0
将允许无限重定向。
$options = new Options(); $options->enableFollowLocation(); //Abort after 10 redirects $options->setFollowLocationLimit(10);
对于包括响应中的Header
$options = new Options(); $options->setIsHeadersIncludedInResponse(true);
URL检查
URL检查方法是公开的,这意味着您可以在应用程序的其它地方使用之前验证URL,尽管您可能希望捕获任何重定向。
use fin1te\SafeCurl\Url; use fin1te\SafeCurl\Exception; try { $url = 'http://www.google.com'; $validatedUrl = Url::validateUrl($url); $fullUrl = $validatedUrl['url']; } catch (Exception $e) { // URL wasn't safe }
可选保护
除了标准检查之外,还有两种可选的保护措施。
第一种是防止DNS重绑定攻击。这可以通过在Options
对象上调用enablePinDns
方法来启用。这个问题的主要问题之一是SSL证书无法验证。这是由于在Host
头中发送了真实的主机名,并且URL使用IP地址。
$options = new Options(); $options->enablePinDns();
对于PinDns - 执行需要两个参数,url和headers。
$options = new Options(); $options->enablePinDns(); $safeCurl = new SafeCurl($curlHandle, $options); $response = $safeCurl->execute($url, $headers);
第二种是禁用在URL中使用凭据,因为PHP的parse_url
返回的值与cURL使用的值不同。这是一个临时修复。
use fin1te\SafeCurl\SafeCurl; use fin1te\SafeCurl\Exception; use fin1te\SafeCurl\Options; $options = new Options(); $options->disableSendCredentials(); $curlHandle = curl_init(); //This will throw an InvalidURLException $safeCurl = new SafeCurl($curlHandle, $options); $response = $safeCurl->execute('http://user:pass@google.com');
注意事项
由于SafeCurl使用gethostbynamel
解析域名,而该函数与IPv6不兼容,因此类目前只能与IPv4一起使用。请参阅问题#1。
如上所述,我们不能获取针对提供的cURL句柄设置的任何cURL选项的值。因为SafeCurl会自行处理重定向,所以它会关闭CURLOPT_FOLLOWLOCATION
并使用Options
对象中的值。对于CURLOPT_MAXREDIRECTS
也是如此。