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

Обход аутентификации и атаки на сессии

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

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

На авторизованном задании тестировщик входит в целевое приложение, копирует из браузера свой собственный токен сессии, выходит из учётной записи и вставляет токен обратно в свежий запрос. Приложение отвечает так, будто он по-прежнему залогинен. Одно это наблюдение говорит обо всём: сервер никогда не привязывал токен к самому акту входа — он привязал личность к обладанию строкой. С кресла атакующего пароль никогда не был интересной частью. Пароль проверяется ровно один раз, у стены входа, под лимитами частоты, блокировками и, возможно, MFA. А сессия — bearer-учётка, которую сервер выдаёт в ответ и затем доверяет на каждом последующем запросе — проверяется постоянно, ездит повсюду и обычно охраняется куда небрежнее, чем стена, которую она заменяет. Поэтому реальный угон аккаунта — это редко «угадай пароль». Это украсть доказательство, подделать доказательство или обманом заставить браузер жертвы потратить доказательство за тебя. Этот урок — карта атакующего по этим трём ходам: обход, угон и подделка, прочитанная как вскрытие, которое защитник делает над собственным auth-кодом.

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

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

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

Форма проблемы: где живёт доказательство

Аутентификация и управление сессиями — две разные задачи, которые новички сливают в одну. Аутентификация — одноразовое событие у стены входа: докажи, что ты тот, кем себя называешь, обычно паролем плюс, возможно, вторым фактором. Управление сессией — всё, что после: сервер выдаёт учётку — идентификатор сессии в cookie или самодостаточный токен вроде JWT — и затем доверяет этой учётке как «это тот пользователь, что вошёл» на каждом следующем запросе, потому что сам HTTP не имеет состояния и ничего не помнит.

Эта передача — мягкая цель. Стена входа закалена: лимиты частоты, блокировки, MFA, проверки на утёкшие пароли. А учётка сессии, которую стена производит, — это bearer-токен: кто держит его, того и считают пользователем — и она переигрывается на сотнях запросов, кэшируется в браузерах, иногда логируется, иногда попадает в URL. Атакующий, который никогда не трогает пароль, но получает, предсказывает или фабрикует эту учётку, имеет тот же доступ, что и легитимный пользователь. Три семейства атак целят в этот шов:

  • Обход аутентификации — достичь авторизованного состояния, не завершив вход.
  • Угон сессии — захватить валидную сессию, принадлежащую кому-то другому.
  • Подделка сессии — изготовить учётку, которую сервер ошибочно примет (ловушки JWT).

CSRF — четвёртый ход с подвохом: атакующий вообще не держит учётку — он заставляет собственный браузер жертвы потратить её.

Обход аутентификации: попасть внутрь без стены

Обход — это когда атакующий достигает авторизованного состояния, не удовлетворив проверку, которую разработчик считал обязательной. Повторяющаяся корневая причина та же, что у более широкого сбоя контроля доступа: приложение предполагает, что шаг произошёл, вместо того чтобы проверить, что он произошёл.

  • Принудительный обход / отсутствие проверок на уровне функций. Форма входа закрывает ссылку на /dashboard, но сам /dashboard никогда заново не проверяет сессию, поэтому прямой запрос к нему входит насквозь. «Аутентификация» была спрятанной дверью, а не замком.
  • Логические изъяны в многошаговых потоках. Сброс пароля шлёт токен на почту, но эндпоинт «задать новый пароль» принимает имя пользователя и новый пароль, не привязывая их к валидному, непогашенному, неистёкшему токену — поэтому атакующий сбрасывает любой аккаунт. Или многошаговый вход ставит флаг «прошёл первый фактор», который клиент может перещёлкнуть, минуя MFA.
  • Доверие к управляемому клиентом состоянию. Запрос несёт role=admin или isAuthenticated=true в cookie, заголовке или claim’е JWT, который сервер читает, не выводя его заново на своей стороне. Клиент владеет всем, что отправляет; считать любое из этого проверенным фактом — и есть баг.

Зрелый рефлекс — запрет по умолчанию и проверка каждого шага на стороне сервера: каждый защищённый маршрут независимо заново устанавливает, кто вызывающий и что ему можно, вычисляя это из серверного состояния сессии, — никогда не выводя из того, что маршрут стоит «за» страницей входа, и не из значения, присланного клиентом.

Угон сессии: кража валидной учётки

Угон захватывает сессию, которая по-настоящему валидна, — атакующий ничего не подделывает, он получает настоящий bearer-токен. Интересный для защитника вопрос — как токен утекает, потому что у каждого пути утечки есть точный структурный фикс.

АтакаКак добывается учёткаСтруктурный фикс
Сниффинг в транзитеТокен считан с провода на нешифрованном или пониженном соединенииTLS везде + HSTS; флаг cookie Secure, чтобы она не уходила по HTTP
Кража через XSSВнедрённый скрипт читает cookie или токен из JSCookie HttpOnly (JS не читает) + чинить XSS в корне
Фиксация сессииАтакующий подсаживает известный ему id сессии, жертва входит под нимПерегенерировать id сессии при каждой смене привилегий (вход)
ПредсказаниеId сессий последовательны или низкоэнтропийны, потому угадываемыКриптослучайные id с высокой энтропией из CSPRNG
Токен в URLСессия утекает через Referer, историю браузера, логи, общие ссылкиНикогда не класть токены сессии в URL; держать в cookie/заголовках

Две из них достойны более пристального взгляда. Фиксация сессии переворачивает обычный порядок: вместо кражи токена, который у жертвы уже есть, атакующий даёт жертве id сессии, который сам уже знает (через подготовленную ссылку или подсаженную cookie), ждёт, пока жертва аутентифицируется, и — если сервер сохраняет тот же id через вход — наследует аутентифицированную сессию. Фикс — одна строка дисциплины: ротировать идентификатор сессии при каждом переходе привилегий, чтобы id, под которым жертва аутентифицируется, был новеньким и неизвестным атакующему. HttpOnly и XSS — пара: хранимый XSS, способный выполнить document.cookie, превращает сессию каждого посетителя в кражу, поэтому HttpOnly (cookie невидима для JavaScript) — это локализация, отвязывающая «у нас есть баг XSS» от «баг XSS осушает каждую сессию» — хотя это локализация, а не лечение самого XSS.

Ловушки JWT: подделка учётки, которую сервер примет

JWT — это самодостаточная учётка: claim’ы (кто ты, твоя роль, срок) едут внутри токена, и сервер доверяет им, потому что токен подписан. Этот дизайн переносит границу доверия на подпись — ровно туда, где живут классические подделки. Со стороны атакующего вопрос всегда один: могу ли я заставить сервер принять токен, который я сформировал?

  • Ловушка alg: none. Ранние библиотеки JWT уважали заголовок, объявляющий алгоритм none — «этот токен не подписан» — и принимали его как валидный. Атакующий правит payload (например, role: admin), ставит алгоритм none, выбрасывает подпись, и сервер доверяет токену, который он на деле никогда не проверял. Фикс: сервер фиксирует ожидаемый алгоритм; он никогда не даёт заголовку токена выбирать, как его проверять.
  • Путаница алгоритмов (RS256 в HS256). Когда проверка использует асимметричный ключ (RS256), публичный ключ по замыслу публичен. Если сервер наивно проверяет тем алгоритмом, что называет заголовок, атакующий переключает заголовок на HS256 (симметричный) и подписывает поддельный токен этим публичным ключом как секретом HMAC — который сервер затем «проверяет» тем же публичным значением. Фикс тот же: фиксировать алгоритм на стороне сервера; никогда не выводить его из управляемого атакующим ввода.
  • Слабый или утёкший секрет. Токен HS256, подписанный угадываемым секретом (secret, changeme), брутфорсится офлайн; как только секрет известен, атакующий чеканит любые токены. Фикс: длинные, случайные, управляемые секрет-менеджером ключи — и ротация.
  • Игнор exp / нет отзыва. Самодостаточный токен валиден до истечения, и серверного списка для его отзыва нет. Если сервер забывает проверять exp, перехваченный токен годен вечно; даже когда проверяет, украденный токен пригоден до истечения. Фикс: принудительный срок, короткие времена жизни и связка с механизмом refresh/denylist для выхода и отзыва.
Почему это работает

Почему «пусть токен сам говорит серверу, как его проверять» — это баг за обоими: ловушкой alg: none и путаницей RS256 в HS256? Потому что это отдаёт атакующему контроль над процедурой проверки, а не только над данными. Подпись осмысленна, лишь если проверяющий независимо решает, как выглядит валидная подпись — какой алгоритм, какой ключ. В тот миг, когда сервер читает алгоритм из собственного (правимого атакующим) заголовка токена и подчиняется ему, токен сам себе ставит оценку: он может объявить себя неподписанным или объявить себя подписанным HMAC, чтобы публичный ключ стал «секретом». Структурный фикс в обоих случаях один и не имеет отношения к патчу версии библиотеки: сервер фиксирует ожидаемый алгоритм и ключ вне диапазона запроса и считает поддельным любой токен, который не совпадает. Проверка должна быть свойством, которое утверждает сервер, а не параметром, который поставляет учётка, — тот же принцип запрета по умолчанию, что проходит через каждый auth-контроль.

CSRF: заставить браузер жертвы потратить учётку

CSRF — изящнейшая, потому что атакующий ничего не крадёт и не подделывает — он эксплуатирует собственную услужливость браузера. Браузер автоматически прикрепляет cookie к любому запросу на сайт, включая запрос, инициированный другим сайтом. Поэтому если жертва залогинена в bank.example и заходит на страницу атакующего, скрытая форма, автоотправляющая POST bank.example/transfer, едет вместе с настоящей cookie сессии жертвы, и банк видит полностью аутентифицированный, безупречно валидный запрос — инициированный атакующим. Сессию не компрометировали; её окружающую власть (ambient authority) позаимствовали.

Структурный фикс бьёт прямо в пробел: сервер должен требовать доказательства, что запрос пришёл от его собственного фронтенда, чего окружающие cookie дать не могут. Два механизма, в идеале слоями:

  • Анти-CSRF токены — сервер вшивает непредсказуемый, посессионный токен в собственные формы и требует его на запросах, меняющих состояние. Межсайтовая страница атакующего не может прочитать этот токен (политика одного источника блокирует), поэтому не может его вложить, и поддельный запрос отклоняется.
  • Cookie SameSiteSameSite=Lax или Strict велит браузеру не слать cookie сессии на межсайтовых запросах вообще, убирая окружающую власть, что и делает CSRF возможным. Lax — сильное недорогое значение по умолчанию; токены остаются ремнём к подтяжкам SameSite.

Заметь связь: CSRF работает именно потому, что легитимная сессия валидна, так что «закали вход» ничего для него не делает. Он защищается на слое приёма запроса, а не на слое аутентификации — ровно поэтому это своя категория.

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

У банка `POST /transfer` проходит всякий раз, когда присутствует валидная cookie сессии, без иной проверки. Тестировщик демонстрирует CSRF: скрытая автоотправляющая форма на странице атакующего двигает деньги, используя собственную залогиненную сессию жертвы. Выбери фикс, закрывающий класс.

Викторина

Библиотека JWT принимает токен, чей заголовок объявляет алгоритм как `none`, считая его валидным неподписанным токеном. Почему это эксплуатируемо и каков корневой фикс?

Викторина

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

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

Упорядочи, как авторизованный тестировщик рассуждает от целевого входа к угону аккаунта через сессию, затем к устойчивому фиксу:

  1. 1 Аутентифицироваться, затем найти bearer-учётку, которой сервер доверяет после (cookie сессии или JWT)
  2. 2 Прощупать, как защищена учётка: привязана ли к входу, подписана ли верно, ограничена ли одним источником?
  3. 3 Эксплуатировать слабое звено — переиграть ещё валидный токен, подделать JWT или проехать на сессии через CSRF
  4. 4 Закрыть класс структурно: ротировать id на входе, фиксировать алгоритм JWT на сервере, требовать анти-CSRF доказательство
Вспомните перед уходом
  1. 01
    Почему атакующий целит в сессию, а не в пароль, и каковы три семейства атак на сессию плюс CSRF?
  2. 02
    Объясни ловушки подделки JWT и единый принцип, который их чинит, плюс как работает CSRF и почему закалка входа его не останавливает.
Итог

Аутентификация подтверждает, кто ты, один раз — у закалённой стены входа; управление сессией — всё, что после: сервер выдаёт bearer-учётку (id сессии в cookie или самодостаточный JWT) и доверяет ей на каждом следующем запросе, потому что HTTP без состояния. Этот шов, а не пароль, и есть место, где реально случается угон аккаунта. Обход аутентификации попадает внутрь, не завершив вход: принудительный обход маршрутов, которые не перепроверяют, логические изъяны в потоках сброса или MFA, или доверие к роли и состоянию входа, которыми управляет клиент, — чинится запретом по умолчанию и проверкой каждого шага на сервере. Угон сессии крадёт по-настоящему валидный токен сниффингом, XSS, фиксацией, предсказанием или утечкой в URL, и у каждого пути свой точный структурный фикс: TLS плюс HSTS и cookie Secure, cookie HttpOnly, ротация id сессии при каждой смене привилегий, высокоэнтропийные id и никаких токенов в URL. Подделка JWT идёт от того, что токену позволяют выбирать свою проверку — ловушка алгоритма none и путаница RS256 в HS256 падают перед одним правилом: сервер фиксирует алгоритм и ключ и считает любое несовпадение подделкой, рядом с сильными секретами и принудительным сроком. CSRF не нужна украденная учётка вовсе; он заставляет браузер жертвы потратить её собственную сессию через автоматически прикрепляемую cookie, поэтому защищается на приёме запроса анти-CSRF токенами и SameSite, а не у стены входа. Поэтому в следующий раз, читая auth-код, твой рефлекс — вывернутый наизнанку вопрос атакующего: после входа чему именно доверяет этот сервер — и может ли строки, которую клиент держит, формирует или на которой едет, хватить, чтобы быть пользователем?

Практика

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

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

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

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

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

Trademarks belong to their respective owners. Editorial reference only.