PHP请求伪造攻击及防护(SSRF)

一、什么是服务器请求伪造(SSRF)

服务器请求伪造(Server-Side Request Forgery,SSRF)。

由攻击者构造的攻击链接传给服务端执行造成的漏洞,一般用来在外网探测或攻击内网服务。

简单示例:

利用file协议查看文件
curl -v 'file:///etc/passwd'
curl -v 'http://www.yb.com:80/sec/ssrf.php?url=file:///etc/passwd'

利用dict探测端口
curl -v 'dict://127.0.0.1:80/info'
curl -v 'http://www.yb.com:8082/ssrf.php?url=dict://127.0.0.1:22'

二、如何进行SSRF攻击

https://joychou.org/web/phpssrf.html

https://www.cnblogs.com/flokz/p/weblogic_SSRF.html

https://www.cnblogs.com/flokz/p/ssrf.html

三、PHP中的SSRF漏洞防御

1、没有特殊必要的情况下,不要从用户那里接收要访问的url(不相信任何用户输入原则),防止用户自行构建 URL 进行穿透访问。

2、开启PHP的 open_basedir 配置,将 PHP 的访问限制在特定目录下,禁止PHP随意访问服务器任意路径

#在php.ini中设置,限定PHP的访问目录为/data
open_basedir = /data

开启 open_basedir 可以有效防止 file_get_contents、curl、fopen 等函数对服务器敏感文件的访问。

3、如果必须接收用户传递的 url,则可以使用白名单机制

<?php
declare(strict_types = 1);

$url = $_GET['url'];

// 请求url信息
$urlInfo = parse_url($url);

// 限制请求协议,仅允许http和https请求,防止类似file:///、gopher://、ftp://等引起的安全问题
$validSchemeList = ['http', 'https'];

// 限制允许请求到的地址
$validHostList = ['www.baidu.com'];

// 限制请求的端口为http常用的端口
$validPortList = ['80', '443'];

// 只允许用户访问特定的文件类型
$validTypeList = ['html', 'json', 'gif', 'png', 'jpeg', 'jpg'];

// 不允许访问本地指定的host(避免被用来获取内网数据)
$invalidHostList = ['127.', 'localhost', '192.', '10.'];

if (!in_array($urlInfo['scheme'], $validSchemeList, true)) {
    die('访问协议非法');
}

if (!in_array($urlInfo['port'], $validPortList, true)) {
    die('访问端口非法');
}

if (!in_array($urlInfo['host'], $validHostList, true)) {
    die('请求地址非法');
}

$visitType = pathinfo($url, PATHINFO_EXTENSION);
if (!in_array($visitType, $validTypeList, true)) {
    die('访问类型非法');
}

foreach ($invalidHostList as $invalidHost) {
    if (strpos($urlInfo['host'], $invalidHost) == 0) {
        die('访问地址非法');
    }
}