Сервис принимает от пользователя URL для загрузки превью. Как проверить, не превращается ли эта функция в SSRF?
Проверка должна установить, может ли сервер по управляемому пользователем URL отправлять запросы во внутренние сети, к локальному хосту или к облачным служебным адресам. Для этого используют контролируемый внешний endpoint, проверяют сетевые обращения сервера и отдельно исследуют обходы фильтрации адресов.
Недостаточно убедиться, что URL имеет допустимую схему или не содержит очевидный внутренний адрес. SSRF возникает из-за доверия серверной части к адресу, который фактически выбирает атакующий.
Функции предварительного просмотра, импорта по ссылке, проверки вебхуков и загрузки удалённых ресурсов появились как способ переложить сетевую работу с клиента на сервер. Серверу это удобно: он имеет стабильный доступ к внешним ресурсам и может обработать результат единообразно.
Проблема возникает, когда такой серверный запрос получает доступ к сетевому окружению приложения. Внутренние панели, базы данных, служебные API и метаданные облачной инфраструктуры обычно не рассчитаны на обращения из недоверенного пользовательского контекста.
Тестировщик передаёт URL на контролируемый домен и проверяет, действительно ли запрос выполняется сервером, а не браузером. Затем исследуются попытки обращения к локальному хосту, частным сетям, loopback-адресам, адресам привязки всех интерфейсов и служебным адресам среды размещения.
Опасность состоит не только в чтении ответа. Уязвимый сервер может отправить запрос во внутреннюю систему, вызвать действие с его сетевыми полномочиями или раскрыть содержимое ответа пользователю. Даже если тело ответа скрыто, факт успешного соединения иногда позволяет проводить сканирование внутренней сети по различиям во времени ожидания и кодах ошибок.
Сначала проверяют базовый сценарий: URL указывает на тестовый внешний ресурс, который фиксирует источник запроса, метод, заголовки и время обращения. Если запрос приходит с сервера приложения, это подтверждает наличие серверной загрузки и определяет её сетевую точку выхода.
Затем проверяют, как приложение обрабатывает адреса из запрещённых диапазонов. Важно учитывать не только строковое значение URL, но и результат разрешения DNS, последующие перенаправления, альтернативные представления адреса, IPv4 и IPv6, а также повторное разрешение имени во время соединения.
Надёжная защита строится по принципу разрешённого списка: приложение принимает только необходимые схемы, домены и порты, а сетевой слой дополнительно запрещает исходящие соединения к внутренним диапазонам. Одной проверки текста URL недостаточно: домен может разрешиться во внутренний адрес, а внешний ресурс — перенаправить запрос внутрь сети.
Нужно ограничить число перенаправлений, таймаут, размер ответа, допустимые порты и объём ресурсов. Полезны изоляция компонента загрузки, отдельные сетевые права и отсутствие доступа к служебным учётным данным. При этом блокировка всех исходящих соединений безопаснее, но может сломать требуемую бизнес-функцию; разрешённый список сложнее сопровождать, зато точнее соответствует необходимости.
Проверка считается неполной, если тестируется только HTTP и только один формат адреса. Следует также определить, поддерживаются ли другие схемы, как обрабатываются ошибки DNS и TLS, сохраняются ли загруженные ответы и может ли пользователь влиять на заголовки или метод запроса.
Сервис загружал изображения по URL для генерации превью. Внешние адреса обрабатывались корректно, поэтому команда рассматривала два варианта: добавить несколько строковых запретов для локальных адресов или вынести загрузку в отдельный компонент с разрешённым списком доменов и сетевыми ограничениями.
Первый вариант был проще, но не учитывал DNS-перенаправления, IPv6 и последующие перенаправления. Второй требовал изменений инфраструктуры и согласования списка поставщиков, зато ограничивал не только ввод, но и фактические сетевые возможности процесса.
Выбрали изолированный загрузчик: он принимал только нужные домены и схемы, не имел доступа к внутренним сетям, ограничивал размер и время загрузки, а перенаправления проверялись на каждом шаге. В результате внешний функционал сохранился, а попытки обратиться к внутренним адресам блокировались независимо от их текстового представления.
Нет. Имя домена может сначала разрешиться во внешний адрес, а затем при повторном разрешении — во внутренний, либо внешний сервер может вернуть перенаправление на запрещённый адрес. Проверять нужно конечный адрес каждого соединения и повторять проверку после перенаправления; ещё надёжнее — дополнить это сетевым запретом исходящих соединений.
Да. Сервер всё равно может отправить запрос во внутреннюю систему и вызвать побочный эффект: изменить состояние, запустить операцию или проверить доступность узла. Кроме того, различия в кодах ошибок, задержках и размере ответа могут раскрывать сведения о внутренней сети, поэтому отсутствие прямого отображения тела ответа снижает ущерб, но не устраняет уязвимость.
Разрешённый домен может быть скомпрометирован, перенаправлять запросы или разрешаться в неожиданные адреса. Поэтому нужно контролировать схему, порт, результат DNS-разрешения, каждый переход по перенаправлению и фактическое сетевое соединение. Дополнительная изоляция загрузчика и запрет доступа к внутренним сетям уменьшают последствия ошибки в логике фильтрации.