open atlas
↑ К треку
Наступательная безопасность RED · 02 · 03

SSRF и IDOR

SSRF и IDOR — современная пара с высоким импактом: один заставляет сервер сходить по выбранному атакующим URL, другой возвращает запись по id без проверки владельца. Корневая ошибка одна — граница доверия, которую сервер не проконтролировал. Разбери, как работает каждый.

RED Middle ◷ 15 min
Уровень
ОсновыJuniorMiddleSenior

На авторизованном тесте тестировщик находит страницу профиля с полем «импортировать аватар по URL». Вместо картинки он вставляет ссылку на адрес облачного metadata-сервиса. Через секунды HTTP-клиент приложения возвращает текст — временные облачные учётные данные, подписанные и готовые к использованию. Он не трогал периметр сети, не обошёл файрвол, не взломал ни одного пароля. Он просто попросил сервер сделать запрос от его имени, и сервер, сидящий внутри доверенной сети с прикреплённой IAM-ролью, послушно сходил. Это SSRF: атакующий не дотягивается до внутреннего сервиса — сервер дотягивается до него за атакующего. На том же тесте он открывает свой заказ GET /api/orders/7741, меняет номер на 7742 и читает чужой адрес доставки и сумму заказа — без ошибки, без тревоги, потому что запрос гласил WHERE id = ? и доверял валидной сессии как авторизации. Это IDOR. Два бага, две клетки на карте OWASP, одна и та же корневая ошибка: сервер доверился значению, которое должен был проверить.

К концу урока ты сможешь объяснить, как атакующий рассуждает об SSRF и IDOR, почему оба — на самом деле один и тот же сбой границы доверия, и какой серверный проектный рефлекс закрывает оба.

Сначала авторизация: это вскрытие глазами защитника

Всё ниже предполагает подписанный договор с явно очерченной областью — намеренно уязвимый стенд, CTF, твоё собственное приложение или программу bug-bounty с целью в области. Нацелить серверную функцию запросов на чужой облачный metadata-эндпоинт или перебирать id объектов в приложении, на которое у тебя нет авторизации, — это несанкционированный доступ в большинстве юрисдикций, без вариантов. Прочесть чужую запись — нарушение приватности, даже если ты «только посмотрел». Мы изучаем рассуждения атакующего по одной причине: нельзя надёжно закрыть класс уязвимостей, который понимаешь лишь как строку в чек-листе. Читай это как вскрытие, которое защитник проводит над собственным кодом, — учась видеть запрос и SQL-запрос так, как их видит атакующий, чтобы контроль оказался в проекте до того, как баг уедет в прод. Никаких готовых пейлоадов — только механизм.

SSRF: сервер как сбитый с толку посредник

Server-side request forgery возникает, когда приложение берёт URL (или хост, или IP), на который влияет пользователь, и делает по нему исходящий запрос — функции «загрузи эту картинку», «импортируй из этого вебхука», «покажи превью этой ссылки», «провалидируй этот RSS-фид». Ввод атакующего никогда не достигает внутренней цели напрямую — вместо этого сервер обманом заставляют до неё дотянуться. Это классический сбитый с толку посредник (confused deputy): у сервера есть привилегии и сетевая позиция, которых нет у атакующего — он сидит внутри VPC, резолвит внутренний DNS, держит IAM-роль, — и атакующий заимствует всё это, выбирая, куда сервер направит запрос.

У механизма две составляющие. Во-первых, назначение запроса под влиянием атакующего, и сервер не ограничивает его известным-хорошим множеством. Во-вторых, сеть, в которой живёт сервер, содержит нечто ценное, до чего атакующий не дотянется снаружи: облачный metadata-сервис инстанса, внутреннюю админ-панель на приватном IP, базу, принимающую соединения только с прикладного слоя, K8s API на cluster-IP. Хрестоматийный приз — облачный metadata-эндпоинт по link-local адресу 169.254.169.254, который на неправильно настроенном инстансе раздаёт IAM-учётки машины всякому, кто спросит изнутри хоста. SSRF дотягивается до него, потому что запрос теперь исходит изнутри хоста.

Рассуждение атакующего — серия проб. Следует ли функция за редиректами (чтобы allowlist-хост 302-редиректнул на внутренний)? Резолвит ли она DNS в момент запроса (чтобы имя, резолвящееся в публичный IP при валидации, перерезолвилось в 127.0.0.1 при запросе — гонка DNS-rebinding)? Принимает ли схемы кроме http, вроде file://, gopher:// или dict://, превращающие запрос в чтение файла или сырую запись в Redis? Каждое «да» — ещё один обход наивной защиты. Поэтому SSRF и попал в OWASP Top 10 голосованием практиков, несмотря на скудные исторические данные: облачные архитектуры сделали радиус поражения огромным — одна функция запроса может стать полной компрометацией облачного аккаунта.

IDOR: отсутствующая проверка владельца

Insecure Direct Object Reference — это продакшен-лицо Broken Access Control. Сервер выставляет ссылку на внутренний объект — чаще всего id строки БД в URL вроде /api/orders/7741, — использует это присланное клиентом значение для поиска объекта и никогда не проверяет, что вызывающий вправе видеть именно этот объект. Запрос — SELECT … WHERE id = ?, когда должен был быть SELECT … WHERE id = ? AND owner_id = ?. «Эксплойт» атакующего — инкремент целого числа. Ни пейлоада, ни особого инструмента: валидная сессия плюс угадываемый id — и это весь штурм.

Ключевое переосмысление: IDOR — это авторизация, а не аутентификация. Атакующий полностью залогинен как он сам — в этом и суть. Аутентификация спрашивает «кто ты?», и сессия отвечает верно. Авторизация спрашивает «вправе ли ты трогать этот объект?», а уязвимый код её вовсе не задаёт — он молча считает, что валидная сессия даёт право на любой id, который ты назовёшь. Ссылки на объекты утекают постоянно — в URL, логах, заголовках referer, расшаренных ссылках, прошлых ответах API, — поэтому «сделать id случайными UUID» лишь повышает стоимость угадывания; недостающую проверку это не добавляет. Вот зрелая ловушка, которой надо избежать: неугадываемые идентификаторы — это обфускация, а не контроль доступа.

Один корень, зеркальная форма

Поставь SSRF и IDOR рядом — и симметрия и есть урок. В обоих сервер берёт значение от клиента и действует по нему, не контролируя подразумеваемую границу. IDOR доверяет ссылке на объект как «тебе можно прочитать этот объект». SSRF доверяет URL как «тебе можно сходить за этим ресурсом». Оба подменяют непроверенным клиентским значением серверное решение об авторизации. Таблица делает параллель явной.

АспектIDORSSRF
Доверенное значениеid объекта в запросеURL / хост, куда ходит сервер
Недостающая проверкаВладеет ли вызывающий этим объектом?Разрешено ли это назначение?
Действие атакующегоИнкремент / подмена idНавести URL на внутреннюю цель
Категория OWASPA01 Broken Access ControlA10 SSRF
Худший случайМассовое чтение/правка чужих данныхMetadata-учётки → захват облака
Корневой фиксАвторизация по владельцу, deny-by-defaultAllowlist egress + укреплённый metadata
Ложный фиксСлучайные UUID-id (обскурность)Blocklist «плохих» IP-строк
Почему это работает

Почему blocklist плохих адресов проваливается для SSRF там, где allowlist хороших хостов срабатывает? Потому что адресное пространство, которое атакующий может выразить, практически безгранично и неоднозначно: 169.254.169.254 говорит и как десятичное 2852039166, и как hex 0xA9FEA9FE, и как восьмеричное, и как IPv6-mapped формы, и как DNS-имя, резолвящееся в него, и как allowlist-хост, который 302-редиректит на него. Blocklist обязан перечислить каждое написание «плохого» и проигрывает в момент, когда пропустит хоть одно; allowlist перечисляет малое конечное множество назначений, куда ты реально намерен ходить, и по умолчанию отказывает всему остальному. Это тот же принцип deny-by-default, что и у IDOR: начинай с «нет» и разрешай немногие известные-хорошие случаи, а не начинай с «да» и гоняйся за каждым известным-плохим.

Как защитник закрывает оба

Для IDOR фикс структурный, не косметический: проверяй авторизацию на сервере по самому объекту и действию, каждый раз, вычисляя её из серверного состояния сессии — никогда не выводя из скрытого UI-элемента или присланного клиентом claim’а. В запросе это значит ограничение по владельцу (WHERE id = ? AND owner_id = ?) или явная проверка политики до возврата строки. Централизуй её, чтобы новый эндпоинт не мог про неё забыть, и сделай авторизацию на уровне объекта тем умолчанием, которое наследует каждый обработчик.

Для SSRF защита слоистая, потому что ни один контроль не достаточен сам по себе. Ограничь назначение allowlist’ом разрешённых хостов/схем, резолвь имя хоста и валидируй резолвленный IP против заблокированных диапазонов (link-local 169.254.0.0/16, loopback, приватные RFC-1918, metadata-IP облака) и перепроверяй после каждого редиректа — валидация одной только строки проигрывает DNS-rebinding и редиректам. Затем считай, что контроль однажды откажет, и укрепи саму цель: требуй metadata-сервис с сессионными токенами (уровня IMDSv2, который блокирует простой GET, на котором держится классический SSRF), дай инстансу минимум нужных IAM-привилегий и поставь egress-правила файрвола, чтобы прикладной слой попросту не мог открывать произвольные исходящие соединения. Повторяющийся зрелый рефлекс для обоих багов один: отказывай по умолчанию, проверяй границу на сервере и никогда не позволяй непроверенному клиентскому значению заменять решение об авторизации.

Выбери лучший вариант

Функция позволяет загрузить аватар, вставив URL; сервер его скачивает. На авторизованном тесте наведение на облачный metadata-IP утекает учётки. Выбери фикс, который реально закрывает SSRF.

Викторина

Залогиненный пользователь меняет `GET /api/orders/7741` на `7742` и читает чужой заказ. Какой это класс и какой именно контроль отсутствует?

Викторина

Почему замена последовательных id заказов на случайные UUID — недостаточный фикс для IDOR?

Расставь шаги по порядку

Упорядочи шаги цепочки SSRF-к-захвату-облака, от ввода атакующего к худшему исходу:

  1. 1 Атакующий подаёт URL в серверную функцию запроса
  2. 2 Сервер делает запрос изнутри сети своей IAM-ролью
  3. 3 Запрос достигает облачного metadata-эндпоинта (169.254.169.254)
  4. 4 Временные IAM-учётки возвращаются атакующему
  5. 5 Атакующий использует учётки для пивота к захвату облачного аккаунта
Вспомните перед уходом
  1. 01
    Объясни механизм SSRF, почему его зовут сбитым с толку посредником и почему allowlist egress бьёт blocklist.
  2. 02
    Как SSRF и IDOR оказываются одной корневой ошибкой и каков отдельный фикс для каждого?
Итог

SSRF и IDOR — современная пара с высоким импактом, и увидеть их как одну ошибку и есть зрелое прозрение: в обоих сервер действует по присланному клиентом значению, не контролируя границу, которую это значение подразумевает. SSRF (OWASP A10) — сбитый с толку посредник: функция «сходи по этому URL» позволяет атакующему позаимствовать сетевую позицию и идентичность сервера, чтобы достичь внутренней цели, классически — облачного metadata-эндпоинта по 169.254.169.254, превращая одну функцию запроса в компрометацию облачного аккаунта. IDOR (продакшен-лицо A01) — отсутствующая проверка владения: сервер ищет запись по присланному клиентом id и не спрашивает, владеет ли ею вызывающий, так что инкремент целого числа читает чужие данные; это отказ авторизации, а не аутентификации. Фиксы зеркалят баги: авторизация по владельцу, deny-by-default для IDOR, и слоистый allowlist назначений по резолвленному IP плюс укреплённый metadata-сервис для SSRF. Отвергай ловушки обскурности — случайные UUID и строковые blocklist. Теперь, видя обработчик, который ищет по присланному клиентом id, или сервер, ходящий по присланному клиентом URL, твой первый вопрос один и тот же: где серверная проверка того, что это значение вообще разрешено?

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 6 завершено
Связанные уроки

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.