SSRF 漏洞介绍
SSRF (Server-Side Request Forgery) 是一种服务器端漏洞,攻击者可以利用它诱使服务器向内部或外部系统发起恶意请求。SSRF 通常用于访问内部资源、扫描内网、绕过防火墙或发起进一步攻击。
漏洞原理
SSRF 漏洞的根源在于服务器未对用户提供的 URL 或输入进行严格验证,导致攻击者可以控制服务器发起的请求。常见场景包括:
- 访问内部服务:攻击者可以访问服务器内部的敏感服务(如数据库、管理接口)。
- 绕过防火墙:通过服务器发起请求,绕过防火墙限制。
- 文件读取:利用
file://
协议读取服务器上的文件。 - 端口扫描:扫描内网中的开放端口和服务。
SSRF 漏洞示例
示例 1:访问内部服务
假设一个应用允许用户输入 URL 来获取图片:
POST /fetch-image HTTP/1.1
Content-Type: application/json
{
"url": "http://internal-service/admin"
}
服务器未验证 URL,直接发起请求:
const fetch = require('node-fetch');
app.post('/fetch-image', (req, res) => {
const url = req.body.url;
fetch(url)
.then(response => response.text())
.then(data => res.send(data));
});
攻击者可以输入内部服务的 URL,获取敏感信息。
示例 2:文件读取
攻击者利用 file://
协议读取服务器文件:
POST /fetch-image HTTP/1.1
Content-Type: application/json
{
"url": "file:///etc/passwd"
}
服务器返回 /etc/passwd
文件内容。
示例 3:端口扫描
攻击者利用 SSRF 扫描内网端口:
POST /fetch-image HTTP/1.1
Content-Type: application/json
{
"url": "http://192.168.1.1:8080"
}
通过响应时间或错误信息判断端口是否开放。
解决方法
-
验证和过滤用户输入
- 对用户提供的 URL 进行严格验证,只允许特定的协议(如
http
、https
)和域名。 - 示例:
const allowedDomains = ['trusted-site.com']; const url = new URL(req.body.url); if (!allowedDomains.includes(url.hostname)) { return res.status(400).send("Invalid URL"); }
- 对用户提供的 URL 进行严格验证,只允许特定的协议(如
-
禁用危险协议
- 禁止使用
file://
、gopher://
、ftp://
等危险协议。
- 禁止使用
-
使用白名单
- 只允许访问预定义的白名单中的 URL。
-
限制请求目标
- 禁止访问内网 IP 地址和保留地址(如
127.0.0.1
、192.168.0.0/16
)。
- 禁止访问内网 IP 地址和保留地址(如
-
使用 DNS 解析
- 解析 URL 的域名,确保其指向合法的外部地址。
-
设置请求超时
- 为请求设置超时时间,防止攻击者利用 SSRF 进行端口扫描。
-
使用代理或中间件
- 通过代理或中间件过滤请求,确保只允许合法的外部请求。
-
日志监控
- 记录所有外部请求,监控异常行为。
示例安全代码
const express = require('express');
const fetch = require('node-fetch');
const app = express();
const allowedDomains = ['trusted-site.com'];
app.use(express.json());
app.post('/fetch-image', (req, res) => {
const url = new URL(req.body.url);
if (!allowedDomains.includes(url.hostname)) {
return res.status(400).send("Invalid URL");
}
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
return res.status(400).send("Invalid protocol");
}
fetch(url)
.then(response => response.text())
.then(data => res.send(data))
.catch(err => res.status(500).send("Error fetching data"));
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
总结
SSRF 漏洞的危害性较大,可能导致内部服务暴露、敏感信息泄露或进一步攻击。通过严格验证用户输入、禁用危险协议、使用白名单等方法,可以有效防止 SSRF 漏洞。同时,结合日志监控和请求过滤,可以进一步增强安全性。