概念
攻击者利用某服务器请求来获取内网或外网系统权限,暴力请求获取服务器端口开发情况等。
攻击流程
- 攻击者构造请求
- 服务器根据攻击者构造的请求对内网服务器进行请求
- 内网服务将请求反馈给服务器
- 服务器将获取到的内网资源返回给攻击者
危害
使服务器资源泄漏,内网服务任意扫描泄漏内网信息。
容易引起SSRF的函数
file_get_contents从用户指定的URL获取文件,如果用户传的URL为/etc/password,则可轻易获得服务器的用户列表。
fsocketopen暴力请求内网端口,通过返回值,判断获得服务器端口的开放情况。
容易造成SSRF的功能
原理其实都是使得网站可以请求攻击者输入的URL
- 页面分享,用户输入希望分享的URL,网站服务端会请求用户输入的URL,解析内容呈现给用户
- 页面转码
- 翻译服务
- 加载服务器上的图片展示给用户
SSRF漏洞防御
核心思想:合理控制访问权限
- 无特殊情况不要接收用户要访问的URL
- 限制PHP随意访问服务器的任意路径。PHP配置中设置开启open_basedir,限定脚本的访问目录
- 如必须接收用户的URL,可使用白名单机制,包括端口白名单、协议白名单、文件类型白名单、甚至是URL白名单;并且要统一处理服务端的返回输出,避免请求的错误信息直接暴露给用户。
- 如果项目中需要获取外网资源,使用黑名单屏蔽内网
代码库封装
<?php
class Filter
{
private $schemeWhiteList = [
'http',
'https',
];
private $hostWhiteList = [
'www.why.com'
];
private $portWhiteList = [
80, 443
];
private $fileTypeWhiteList = [
'html', 'gif', 'png', 'jpeg',
];
private $blackHostList = [
'172.', '10.', 'localhost', '127.', '192.',
];
private $blackIpList = [
'127.', '10.', '127.', '192.',
];
private $info;
public function filterBlackIp($url)
{
$host = $this->getHost($url);
$ip = gethostbyname($host);
if ($ip == $host){
return false;
}
if (empty($this->blackIpList)){
return true;
}
foreach ($this->blackIpList as $blackIp){
if (strpos($ip, $blackIp) === 0){
return false;
}
}
return true;
}
public function filterBlackHost($url)
{
$host = $this->getHost($url);
if (empty($this->blackHostList)){
return true;
}
foreach ($this->blackHostList as $BlackHost){
if (strpos($host, $BlackHost) === 0){
return false;
}
}
return true;
}
public function filterScheme($url)
{
$scheme = $this->getScheme($url);
return in_array($scheme, $this->schemeWhiteList);
}
public function filterPort($url)
{
$port = $this->getPort($url);
return in_array($port, $this->portWhiteList);
}
public function filterHost($url)
{
$host = $this->getHost($url);
return in_array($host, $this->hostWhiteList);
}
public function getPort($url)
{
$info = $this->getInfo($url);
return $info['port'];
}
public function getScheme($url)
{
$info = $this->getInfo($url);
return $info['scheme'];
}
public function getHost($url)
{
$info = $this->getInfo($url);
return $info['host'];
}
public function parseInfo($url)
{
return parse_url($url);
}
public function getInfo($url)
{
if (empty($this->info[md5($url)])){
$this->info[md5($url)] = $this->parseInfo($url);
}
return $this->info[md5($url)];
}
public function getPathExt($url)
{
return pathinfo($url, PATHINFO_EXTENSION);
}
public function filterPathExt($url)
{
$ext = $this->getPathExt($url);
return in_array($ext, $this->fileTypeWhiteList);
}
}