SSRF — Server-Side Request Forgery
SSRF (Server-Side Request Forgery) — атака, при которой злоумышленник заставляет сервер делать HTTP-запросы к произвольным адресам (включая внутреннюю сеть, metadata-сервисы облака, localhost), используя серверные функции загрузки URL.
Зачем нужно
В облачных окружениях SSRF даёт доступ к AWS/GCP metadata API (169.254.169.254) — источнику IAM-токенов, что может привести к полному компрометированию облачного аккаунта. Capital One взлом 2019 года произошёл именно через SSRF в AWS EC2.
Где используется
- Функции "загрузить изображение по URL", "предпросмотр сайта", "webhook"
- Импорт данных из внешних источников по URL
- PDF-генераторы с рендерингом HTML по URL
Основной контент
Пример уязвимого кода
// УЯЗВИМО — загружает любой URL
app.post('/api/preview', async (req, res) => {
const { url } = req.body;
const response = await fetch(url); // Атака: url = "http://169.254.169.254/latest/meta-data/"
const content = await response.text();
res.send(content);
});
Опасные адреса при SSRF
http://169.254.169.254/latest/meta-data/ # AWS EC2 metadata
http://metadata.google.internal/computeMetadata/ # GCP metadata
http://localhost:6379 # Redis без авторизации
http://internal-api.corp.example.com # Внутренние сервисы
http://0.0.0.0/ # Обход localhost-фильтров
http://[::1]/ # IPv6 localhost
Безопасный паттерн: whitelist доменов
const { URL } = require('url');
const dns = require('dns').promises;
const ALLOWED_DOMAINS = new Set(['api.trusted.com', 'cdn.trusted.com']);
async function validateUrl(rawUrl) {
let parsed;
try {
parsed = new URL(rawUrl);
} catch {
throw new Error('Invalid URL');
}
// Только HTTPS
if (parsed.protocol !== 'https:') {
throw new Error('Only HTTPS allowed');
}
// Только разрешённые домены
if (!ALLOWED_DOMAINS.has(parsed.hostname)) {
throw new Error('Domain not allowed');
}
// Проверка DNS — домен не должен разрешаться в приватные IP
const addresses = await dns.lookup(parsed.hostname, { all: true });
for (const { address } of addresses) {
if (isPrivateIP(address)) {
throw new Error('SSRF: private IP detected');
}
}
return parsed.toString();
}
function isPrivateIP(ip) {
return /^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.|127\.|169\.254\.|::1|fc|fd)/.test(ip);
}
SSRFmap — инструмент для тестирования
# Проверка эндпоинта на SSRF
python3 ssrfmap.py -r request.txt -p url -m readfiles
Частые ошибки
- Blocklist IP-адресов вместо allowlist доменов — слишком легко обойти через DNS rebinding
- Проверка hostname без проверки resolved IP — DNS rebinding атака обходит
- Разрешение редиректов при fetch — редирект может вести на internal адрес
- Доверие значению заголовка
HostилиX-Forwarded-Forдля формирования URL