Управление сессиями
После входа сервер выдаёт «пропуск» — и от того, как этот пропуск устроен и хранится, зависит, не воспользуется ли им чужой.
Сессия — состояние «пользователь вошёл», которое сервер связывает с непредсказуемым идентификатором (session ID); этот идентификатор браузер хранит обычно в cookie и присылает с каждым запросом.
Пароль вводят один раз, а дальше всю работу удостоверяет session ID. Поэтому он сам по себе — ценность: кто владеет действующим идентификатором, тот и есть пользователь для сервера. Угнать сессию (session hijacking) часто проще, чем подобрать пароль: достаточно перехватить cookie или подсунуть жертве заранее известный идентификатор. Управление сессиями — это набор мер, которые делают идентификатор трудным для кражи и быстро обесценивают украденный. Все примеры — на своих стендах; перехват чужих сессий наказуем (ст. 272 УК РФ).
Зачем это знать защитнику
Сломанное управление сессиями обнуляет качество остальной защиты: можно идеально хранить пароли и всё равно отдать аккаунт, если cookie доступна из JavaScript или идентификатор не меняется при входе. Разработчик, понимающий жизненный цикл сессии, ставит правильные флаги cookie и ротацию там, где их легко забыть; защитник по этим же признакам быстро оценивает риск.
Безопасные cookie: три флага
Идентификатор сессии почти всегда живёт в cookie, и три атрибута решают, насколько она защищена. Уязвимый вариант — cookie без флагов:
# УЯЗВИМО: cookie без защиты — доступна из JS, ходит по HTTP, шлётся кросс-сайтово
Set-Cookie: session=ab12cd34
HttpOnly запрещает доступ к cookie из JavaScript (document.cookie). Это значит, что даже сработавший XSS не сможет прочитать сессионный токен и отправить его атакующему. Secure разрешает отправлять cookie только по HTTPS — она не уйдёт в открытом виде по HTTP, где её можно перехватить в сети. SameSite ограничивает отправку cookie на сторонние сайты: Lax (разумный дефолт) или Strict заметно снижают риск CSRF, потому что cookie не прикрепляется к запросам, инициированным чужим сайтом. Безопасный набор:
# БЕЗОПАСНО: недоступна из JS, только HTTPS, ограничена кросс-сайтово
Set-Cookie: session=...; HttpOnly; Secure; SameSite=Lax; Path=/
В коде это обычно настройка фреймворка, а не ручная сборка заголовка:
# БЕЗОПАСНО: безопасные атрибуты cookie сессии (пример настроек)
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SECURE = True # только HTTPS
SESSION_COOKIE_SAMESITE = "Lax"
Фиксация сессии и ротация при логине
Фиксация сессии (session fixation) — атака, при которой жертве заранее навязывают известный атакующему session ID, и если после входа сервер не меняет идентификатор, атакующий оказывается внутри уже аутентифицированной сессии.
Логика атаки: злоумышленник получает валидный (ещё не залогиненный) идентификатор и заставляет браузер жертвы использовать именно его (например, через ссылку с этим ID). Жертва входит под своим паролем. Если сервер оставляет тот же session ID после успешного логина, то идентификатор, который знает атакующий, теперь привязан к авторизованной сессии — и он входит как жертва, не зная пароля.
Защита прямолинейна и обязательна: при каждой смене уровня привилегий (вход, повышение прав) выдавайте новый идентификатор сессии, аннулируя прежний. Тогда тот ID, что «зафиксировал» атакующий, после логина становится недействительным.
# БЕЗОПАСНО: ротация идентификатора сессии при входе
def login(request, user):
authenticate(user)
request.session.cycle_key() # новый session ID, старый недействителен
# ... отметить пользователя как вошедшего
Симметрично: при выходе сессию нужно уничтожать на сервере, а не просто удалять cookie у клиента. Иначе перехваченный ранее идентификатор продолжит работать.
Тайм-ауты: idle и absolute
Бессрочная сессия — подарок для того, кто украл идентификатор. Поэтому используют два тайм-аута. Idle timeout (по бездействию) завершает сессию после периода без активности — украденный токен «протухнет», если им не пользоваться. Absolute timeout (предельный срок жизни) ограничивает общую длительность сессии независимо от активности, заставляя периодически переаутентифицироваться. Конкретные значения зависят от чувствительности сервиса: для банка — минуты, для форума — дольше. Опция «запомнить меня» должна реализовываться отдельным долговременным токеном с ограниченными правами, а не превращать основную сессию в вечную.
Как это работает под капотом
Сервер хранит у себя сопоставление «session ID → данные сессии» (в памяти, Redis, БД), а клиенту отдаёт только сам идентификатор. Идентификатор должен быть длинным и криптослучайным (генерируется надёжным генератором, а не счётчиком и не предсказуемым значением), иначе его можно угадать. Браузер автоматически прикладывает cookie к каждому запросу на тот же домен — отсюда и удобство, и риск: вся защита сводится к тому, чтобы (1) идентификатор нельзя было предсказать, (2) его нельзя было прочитать или перехватить (HttpOnly, Secure), (3) его нельзя было навязать (ротация при логине) и (4) он быстро терял силу (тайм-ауты, аннулирование при выходе).
Как защититься
- Криптослучайный идентификатор достаточной длины; генерацию доверьте фреймворку, не изобретайте свой.
- Флаги cookie: HttpOnly + Secure + SameSite. Это базовый, не обсуждаемый минимум для сессионной cookie.
- Ротация при логине и при смене прав (защита от фиксации); уничтожение сессии на сервере при выходе и при подозрении на компрометацию.
- Idle и absolute тайм-ауты; «запомнить меня» — отдельным ограниченным токеном.
- HTTPS на всём приложении (HSTS), чтобы cookie не утекала по открытому каналу.
- Обнаружение. Один session ID с разных IP/устройств одновременно, резкая смена User-Agent или географии в рамках сессии — признаки угона. Дайте пользователю список активных сессий и кнопку «завершить все».
Юридическое напоминание: экспериментировать с фиксацией и угоном сессий можно только на своих стендах или в легальном пентесте с разрешением владельца.
Итоги
- Session ID — это «пропуск» после входа; кто им владеет, тот и пользователь, поэтому его берегут.
- Cookie сессии обязана иметь HttpOnly, Secure и SameSite; идентификатор — криптослучайный и длинный.
- Ротация идентификатора при логине закрывает фиксацию сессии; выход должен уничтожать сессию на сервере.
- Idle и absolute тайм-ауты обесценивают украденный токен; «запомнить меня» — отдельный токен.
- Мониторьте аномалии (один ID, разные IP) и дайте пользователю управлять активными сессиями.