Техники перечисления
Перечисление превращает неизвестную поверхность атаки в конкретный список целей — скрытые эндпоинты, реальные пользователи, недокументированные объекты API. Ровно это и призваны замедлить и выявить лимиты запросов и мониторинг.
Лабораторный стенд отвечает на POST /api/login двумя разными сообщениями: «аккаунта с таким email нет» и «неверный пароль». Безобидная формулировка — вот только скрипт теперь может скормить ему список из десяти тысяч email и за несколько минут оставить только те, что вернули «неверный пароль». Он только что подтвердил, у кого из ваших клиентов есть аккаунт, не угадав ни одного пароля. Это и есть перечисление: не взлом, а превращение неизвестной поверхности в точный, ранжированный список того, что стоит атаковать следующим. У формы входа не было бага в привычном смысле. Она просто сказала правду слишком конкретно — а защитник, понимающий перечисление, как раз и замечает, что ответ, тайминг и частота запросов все вместе утекают.
К концу урока вы будете знать, как атакующие перечисляют эндпоинты, пользователей и объекты API в авторизованной лаборатории и как именно лимиты запросов и мониторинг превращают это дешёвое, тихое прощупывание в медленную, дорогую, заметную операцию, которую защитник может поймать.
Сначала граница: только лаборатория, и перечисление — это разведка
Прежде всего о механиках: всё ниже рассчитано на систему, которой вы владеете или которую вам явно разрешено тестировать, — CTF-стенд, намеренно уязвимое приложение в вашей собственной VM, согласованный по объёму проект. Перечисление обращается к реальным эндпоинтам и реальным аккаунтам, поэтому без письменной авторизации и определённого scope это неавторизованный доступ, точка. Здесь нет вооружённых скриптов; цель — понять механизм достаточно глубоко, чтобы обнаруживать и притуплять его на системах, которые вы защищаете.
В последовательности атакующего перечисление — это разведка, первая тактика MITRE ATT&CK (TA0043). Само по себе оно не даёт пробоя. Оно даёт карту: какие пути существуют, какие имена пользователей реальны, какие объекты API можно назвать. Каждая последующая стадия дешевле и тише, потому что перечисление сузило поиск с «всего интернета» до «вот этих сорока вещей». Именно поэтому притупление перечисления имеет несоразмерно высокую защитную ценность — вы повышаете стоимость фундамента, на котором строится вся остальная атака.
Перечисление эндпоинтов: найти то, на что нет ссылок
Первая цель — структура. Карта сайта приложения показывает парадную дверь; перечисление ищет двери, на которые никогда не было ссылок, — /admin, /api/internal, /.git/config, /backup.zip, старую /v1, которую должны были вывести из эксплуатации. Техника — это перебор директорий и контента: берётся список частых путей и имён файлов, каждый запрашивается, и по HTTP-статусу и размеру ответа отличается «это существует» от «этого нет».
Сигнал, который читает атакующий, — это разница в ответах. Аккуратное приложение возвращает 404 на всё, чего нет. Дырявое возвращает 403 Forbidden на /admin (подтверждая, что путь существует, но защищён), 200 на /api/internal/users (существует и не защищён) и редирект 301, тихо раскрывающий реальный путь. Размер ответа важен не меньше статуса: типовая страница 404 имеет фиксированную длину, поэтому 404 с другой длиной часто оказывается реальной страницей в чужом статус-коде. Вся игра — в чтении этих признаков на тысячах запросов.
Перечисление пользователей и API: подтвердить, кто и что существует
Вторая цель — личности и объекты. Перечисление пользователей — это история формы входа из вступления: любое место, где приложение ведёт себя по-разному для реального и фальшивого аккаунта, — это оракул. Классические признаки — отдельное сообщение об ошибке («email не найден» против «неверный пароль»), другой HTTP-ответ и — самый тонкий — разница в тайминге. Если реальный аккаунт запускает дорогое хеширование пароля (bcrypt, argon2), а несуществующий возвращается мгновенно, потому что хешировать нечего, то одно лишь время ответа отделяет реальное от фальшивого даже при одинаковых сообщениях. Потоки регистрации и сброса пароля утекают так же («этот email уже зарегистрирован»).
Перечисление API обобщает это на объекты. REST API по адресу GET /api/orders/1001 напрашивается на вопрос: а что насчёт 1002, 1003, 1000000? Последовательные целочисленные ID позволяют атакующему пройти всю коллекцию простым счётом. Даже когда контроль доступа корректен и каждый запрос отклоняется, шаблон отказов размечает, какие ID существуют (403 на реальный, но запрещённый заказ, 404 на пробел), а подробные тела ошибок, утёкший документ OpenAPI/Swagger или introspection-запрос GraphQL выдают модель объектов целиком.
| Цель | Утечка (оракул) | Что узнаёт атакующий | Защитный фикс |
|---|---|---|---|
| Эндпоинты | Статус/размер различаются (404 vs 403 vs 200) | Какие скрытые пути существуют | Единый 404; deny by default; авторизация до маршрутизации |
| Пользователи (вход) | Отдельное сообщение об ошибке или ответ | У каких email есть аккаунты | Одинаковое общее сообщение для обоих случаев |
| Пользователи (тайминг) | Реальный аккаунт хеширует, фальшивый отвечает мгновенно | Реальный vs фальшивый по времени ответа | Хешировать фиктивный пароль и на ветке промаха |
| Объекты API | Последовательные ID; раскол 403 vs 404 | Какие записи существуют и сколько их | Случайные UUID; одинаковый статус для запрета и отсутствия |
| Форма API | Подробные ошибки, Swagger, introspection GraphQL | Полную модель объектов | Общие ошибки; закрыть доки/introspection в проде |
▸Почему это работает
Почему возврат одной и той же общей ошибки для «пользователь не найден» и «неверный пароль» реально помогает, если реальный атакующий всё равно может перебирать пароли? Потому что это меняет экономику. Без оракула атакующий вынужден разбрасывать учётные данные по неизвестному множеству аккаунтов — большинство попыток тратится на несуществующие email, и каждая впустую — это лишний шум для вашего лимитера и ваших алертов. С оракулом он сначала дёшево перечисляет валидные аккаунты, а затем нацеливает сфокусированную, более медленную атаку по паролям только на реальные цели. Закрытие оракула не останавливает атаку целиком; оно убирает дешёвый разведывательный шаг, который делал дорогой шаг эффективным, — а именно в этом и смысл срыва перечисления.
Что притупляет это: лимиты запросов и мониторинг
Определяющая слабость перечисления — объём. Подтверждение скрытых путей, валидных пользователей или объектов API требует от сотен до миллионов запросов, потому что атакующий ищет в пространстве, а не эксплуатирует одну известную брешь. Вот рычаг, за который тянут защитники.
Лимиты запросов бьют прямо по стоимости. Ограничьте запросы на IP, на аккаунт и на эндпоинт — и перебор, который должен был занять минуты, теперь занимает дни, достаточно долго, чтобы окно проекта закрылось или атакующий ушёл. Зрелый нюанс — где ограничивать: лимиты на IP тривиально обходятся ботнетом или ротацией прокси, поэтому высокоценные оракулы (вход, сброс пароля, эндпоинты токенов) нуждаются ещё и в лимитах на аккаунт и в глобальных потолках, плюс экспоненциальный backoff и CAPTCHA или proof-of-work на подозрительном пути. Лимиты редко делают перечисление невозможным; они делают его достаточно медленным, чтобы оно стало непрактичным, и достаточно громким, чтобы его заметили.
Мониторинг бьёт по скрытности. У перечисления есть отпечаток, которого нет у нормального трафика: один источник, генерирующий поток 404, прогон по последовательным ID, сотни неудачных входов по множеству имён пользователей или частота запросов, которую не выдаёт человек. Алертите на эти паттерны — всплески доли 4xx, доступ к путям или ID высокой кардинальности от одного клиента, всплески неудачной аутентификации — и вы обнаружите стадию разведки до того, как она перерастёт в эксплойт. Это выгода kill chain в миниатюре: перечисление — самая дешёвая, ранняя стадия атакующего, поэтому поймать её здесь — самое дешёвое, раннее место, чтобы разорвать цепочку.
API входа возвращает «аккаунта с таким email нет» для неизвестных email и «неверный пароль» для реальных. Вы укрепляете его против перечисления пользователей. Выберите лучший фикс.
Атакующий запрашивает тысячи угаданных путей и оставляет только те, что не вернули 404. Какое поведение сервера даёт ему больше всего сигнала?
Почему лимиты запросов — настолько эффективный контроль именно против перечисления?
Упорядочьте авторизованную оценку перечислением от первого шага к последнему:
- 1 Подтвердить письменную авторизацию и scope для цели
- 2 Перечислить эндпоинты: перебрать пути, читать разницу статуса/размера
- 3 Перечислить пользователей и объекты API через оракулы сообщения/тайминга/ID
- 4 Ранжировать подтверждённую поверхность по эксплуатируемости для следующей стадии
- 5 Сообщить об оракулах и рекомендовать лимиты запросов + мониторинг
- 01Объясните три главные цели перечисления — эндпоинты, пользователей и объекты API — и конкретный «оракул», который утекает информацию в каждой.
- 02Почему лимиты запросов и мониторинг — правильная защита против перечисления, и в чём зрелый нюанс о том, где применять лимиты?
Перечисление — это шаг разведки, превращающий неизвестную поверхность атаки в конкретный, ранжированный список целей: оно не взламывает, а сообщает атакующему, куда целиться следующим, — поэтому его притупление имеет несоразмерно высокую защитную ценность. У него три главные цели, у каждой свой утекающий оракул: эндпоинты, находимые перебором путей и чтением разницы HTTP-статуса и размера ответа по путям; пользователи, подтверждаемые через отдельные сообщения об ошибке или разницу в тайминге, когда реальный аккаунт запускает дорогое хеширование, а фальшивый отвечает мгновенно; и объекты API, проходимые через последовательные целочисленные ID и выдаваемые целиком подробными ошибками, утёкшими доками Swagger или introspection GraphQL. Всё это работает на объёме, поэтому защита идёт в два хода. Первый — убрать оракулы: возвращать единообразные ответы и общие ошибки, выровнять тайминг хешированием фиктивного пароля на ветке промаха, использовать случайные UUID вместо последовательных ID и закрывать доки API и introspection в проде. Второй — поднять стоимость и видимость: ограничить запросы на IP, на аккаунт и на эндпоинт — помня, что один лишь IP-лимит обходится ботнетами, так что высокоценным эндпоинтам нужны ещё лимиты на аккаунт и глобальные, — и мониторить безошибочный отпечаток перечисления: потоки 404, прогоны по последовательным ID и всплески неудачной аутентификации. Поймайте его на этой стадии — и вы разорвали kill chain на самом дешёвом, раннем звене. Теперь, проектируя эндпоинт, ваш рефлекс — спросить: что каждый ответ раскрывает о том, что существует, и насколько быстро кто-то может задать этот вопрос миллион раз?
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.
Примени это
Примени этот урок в реальном проекте.