SSRF: как заставить сервер атаковать самого себя и почему обычной проверки URL недостаточно

—

от автора

Представим обычный функционал: пользователь вставляет ссылку на изображение, а приложение скачивает его и устанавливает в качестве аватара.

На первый взгляд ничего опасного. Сервер просто отправляет HTTP-запрос по указанному адресу, получает картинку и сохраняет её.

Но есть один нюанс: а что, если вместо ссылки на изображение пользователь передаст адрес внутреннего сервиса, который вообще не должен быть доступен извне?

Например, внутренней административной панели, Redis, Elasticsearch или облачного сервиса метаданных.

Именно на таком принципе работает SSRF — Server-Side Request Forgery. Уязвимость позволяет заставить сервер самостоятельно отправлять запросы туда, куда пользователь напрямую обратиться не может.

Разберём, как возникает SSRF, почему простые фильтры не спасают и какие меры действительно помогают защитить приложение.

Что такое SSRF и откуда берётся проблема

SSRF относится к классу уязвимостей, при которых злоумышленник управляет запросами, выполняемыми серверной частью приложения.

Главная проблема заключается в том, что сервер часто обладает гораздо большим уровнем сетевого доступа, чем обычный пользователь.

Например:

  • Пользователь может обращаться только к публичному веб-сайту.

  • Сервер приложения может обращаться к внутренним API.

  • Внутренние сервисы могут быть доступны только из корпоративной сети.

  • Некоторые облачные сервисы предоставляют конфиденциальные метаданные непосредственно через локальную сеть экземпляра.

Получается интересная ситуация: пользователь не имеет доступа к внутренней инфраструктуре, но может использовать сервер как промежуточное звено.

Как выглядит типичная цепочка SSRF

  1. Пользователь передаёт приложению URL.

  2. Сервер получает этот URL.

  3. Приложение выполняет HTTP-запрос по указанному адресу.

  4. Запрос достигает ресурса, к которому пользователь не имеет прямого доступа.

  5. В зависимости от реализации приложение возвращает ответ пользователю или выполняет действие без отображения результата.

Важно понимать: SSRF не обязательно означает получение содержимого внутреннего сервиса. Иногда достаточно заставить его выполнить определённое действие или определить доступность внутреннего узла.

Где чаще всего встречается SSRF

Уязвимость обычно появляется там, где приложение принимает адрес ресурса и самостоятельно обращается к нему.

Вот несколько типичных сценариев:

1. Загрузка изображений по URL

Пользователь указывает ссылку на картинку, сервер скачивает её и сохраняет в профиле.

2. Webhook-интеграции

Пользователь указывает адрес, на который приложение должно отправлять уведомления о событиях.

3. Генерация PDF

Сервис открывает переданный URL, загружает страницу и преобразует её в PDF.

4. Проверка доступности сайта

Приложение отправляет запрос к указанному адресу и возвращает статус ответа.

5. Импорт внешних данных

Сервер загружает JSON, XML или другие документы по пользовательской ссылке.

Во всех этих случаях появляется потенциальная точка входа для SSRF.

Практический пример: уязвимый обработчик на Python

Посмотрим на упрощённую реализацию сервиса, который скачивает изображения по ссылке.

from flask import Flask, request, jsonifyimport requestsapp = Flask(__name__)@app.route("/fetch-image")def fetch_image():    url = request.args.get("url")    if not url:        return jsonify({"error": "URL is required"}), 400    try:        response = requests.get(url, timeout=5)        return jsonify({            "status": response.status_code,            "size": len(response.content)        })    except requests.RequestException:        return jsonify({"error": "Download failed"}), 400if __name__ == "__main__":    app.run(debug=True)

На первый взгляд код выглядит вполне рабочим:

  • Получаем URL.

  • Отправляем GET-запрос.

  • Ограничиваем время ожидания.

  • Возвращаем статус и размер ответа.

Однако с точки зрения безопасности здесь есть серьёзная проблема.

Приложение никак не ограничивает адреса, к которым разрешено обращаться.

Пользователь может передать не только ссылку на публичное изображение, но и адрес внутреннего ресурса.

Причём отсутствие отображения содержимого ответа не устраняет SSRF. Даже если сервер возвращает только статус и размер ответа, сам запрос уже может повлиять на внутреннюю инфраструктуру.

Почему проверка URL через простые условия не спасает

Первое, что обычно приходит в голову разработчику, — запретить подозрительные адреса.

Например:

if "localhost" in url:    return "Blocked"

Или:

if "127.0.0.1" in url:    return "Blocked"

Проблема в том, что проверяется строка, а не фактическое сетевое назначение.

У адреса могут быть альтернативные представления, доменное имя может разрешаться в неожиданный IP, а HTTP-клиент может самостоятельно следовать перенаправлениям.

Поэтому проверка отдельных подстрок не является полноценной защитой.

DNS rebinding: когда DNS становится частью атаки

Отдельного внимания заслуживает DNS rebinding.

Суть проблемы в том, что доменное имя может разрешаться в разные IP-адреса в разные моменты времени.

Представим следующую ситуацию:

  1. Приложение проверяет домен.

  2. DNS возвращает публичный IP-адрес.

  3. Проверка проходит успешно.

  4. HTTP-клиент повторно разрешает домен.

  5. На этот раз DNS возвращает адрес внутренней сети.

В итоге приложение обращается совсем не туда, куда рассчитывал разработчик.

Это одна из причин, почему недостаточно просто проверить DNS-ответ перед выполнением запроса.

Нужно обеспечить, чтобы HTTP-клиент подключался именно к проверенному IP-адресу, сохраняя корректную проверку имени хоста и TLS-сертификата.

Чем SSRF опасен для облачной инфраструктуры

Особенно неприятно SSRF проявляет себя в облачных окружениях.

Многие облачные платформы предоставляют виртуальным машинам специальные сервисы метаданных (Instance Metadata Service).

Они используются для получения информации об экземпляре, его конфигурации и, в некоторых случаях, временных учётных данных.

Например, в AWS используется адрес:

169.254.169.254

Если приложение имеет SSRF и может обращаться к этому сервису, возникает риск раскрытия конфиденциальной информации и получения временных учётных данных.

Последствия зависят от настроек облачной инфраструктуры и прав выданной роли.

Именно поэтому SSRF нельзя считать просто ошибкой загрузчика изображений: в определённых условиях она становится точкой входа к более серьёзным проблемам инфраструктуры.

Как правильно защищаться от SSRF

Универсальной проверки, которая решает все проблемы SSRF, не существует. Лучше использовать несколько независимых уровней защиты.

1. Использовать Allowlist

Если приложению необходимо обращаться только к определённым сервисам, лучше заранее определить список разрешённых доменов.

Например:

ALLOWED_HOSTS = {    "images.example.com",    "cdn.example.com"}

При этом проверять нужно именно разобранное имя хоста, а не наличие домена в произвольной строке URL.

Если пользовательский функционал не требует произвольных адресов, вообще не стоит предоставлять такую возможность.

2. Проверять IP-адреса

Если приложение должно обращаться к внешним доменам, необходимо проверять все полученные IPv4- и IPv6-адреса.

Для Python можно использовать стандартный модуль ipaddress:

import ipaddressdef is_public_ip(value):    try:        ip = ipaddress.ip_address(value)        return ip.is_global    except ValueError:        return False

Функция позволяет отсеивать адреса, которые библиотека не классифицирует как глобальные.

Но это только один элемент защиты. Необходимо также учитывать DNS rebinding, момент подключения, перенаправления и особенности сетевой инфраструктуры.

3. Отключить автоматические перенаправления

Допустим, приложение обращается к разрешённому внешнему ресурсу.

Но тот отвечает HTTP-редиректом на другой адрес.

Если HTTP-клиент автоматически следует перенаправлению, первоначальная проверка URL уже не гарантирует безопасность конечного назначения.

В библиотеке Requests это можно отключить:

response = requests.get(    url,    timeout=5,    allow_redirects=False)

Если перенаправления необходимы бизнес-логике, каждый новый адрес нужно проверять заново.

4. Ограничить сетевые возможности приложения

Это один из наиболее важных уровней защиты.

Даже если разработчик допустил ошибку в проверке URL, сервер не должен иметь возможность обращаться ко всей внутренней сети.

Для этого применяются:

  • Сетевые ACL.

  • Firewall-правила.

  • Сегментация сети.

  • Ограничения исходящих соединений.

  • Изоляция сервисов в отдельных сетевых сегментах.

Идея проста: приложение должно иметь доступ только к тем ресурсам, которые действительно необходимы для его работы.

5. Использовать безопасные настройки облака

В AWS рекомендуется использовать IMDSv2 и отключать поддержку IMDSv1 там, где это возможно.

IMDSv2 добавляет дополнительный механизм защиты доступа к сервису метаданных.

Однако важно помнить: это дополнительный уровень безопасности, а не замена исправлению SSRF в самом приложении.

Как проверить приложение на SSRF

Для проверки собственной инфраструктуры можно использовать специально созданное тестовое окружение.

Полезные инструменты:

  • Burp Suite — для анализа HTTP-запросов и тестирования пользовательских параметров.

  • OWASP ZAP — для поиска потенциальных веб-уязвимостей.

  • Semgrep — для статического анализа исходного кода и поиска небезопасных конструкций.

  • PortSwigger Web Security Academy — для практики на специально подготовленных лабораторных заданиях.

Особенно полезно проверить все места, где приложение принимает URL или самостоятельно выполняет сетевые запросы.

Стоит обратить внимание на следующие вопросы:

  • Может ли пользователь управлять адресом назначения?

  • Проверяется ли фактический IP-адрес?

  • Учитываются ли IPv6-адреса?

  • Разрешены ли только необходимые протоколы?

  • Отключены ли автоматические редиректы?

  • Защищено ли приложение от DNS rebinding?

  • Ограничены ли исходящие сетевые подключения?

Заключение

SSRF — хороший пример того, как небольшая ошибка в логике приложения может привести к серьёзным последствиям для всей инфраструктуры.

Разработчик хотел добавить удобную функцию загрузки изображений или интеграции с внешним API, но случайно предоставил пользователю возможность управлять сетевыми запросами сервера.

Самая распространённая ошибка при защите от SSRF — попытка решить проблему несколькими проверками строк.

На практике надёжная защита строится на сочетании строгой проверки назначения, безопасной работы с DNS, контроля перенаправлений и сетевой изоляции.

И главное: не стоит считать сервер доверенной стороной только потому, что запрос отправляется изнутри вашей инфраструктуры. Иногда именно он становится инструментом атаки.

Мы в Telegram: https://t.me/+jAjZcfEdOx42NzRi

ссылка на оригинал статьи https://habr.com/ru/articles/1104756/