Все статьи

JWT или Session Cookies: что выбрать для авторизации в веб-приложении

Безопасная аутентификация на Python и FastAPI: JWT-токены, серверные сессии, cookies и управление доступом

14 минут чтения

Представим, что вы разрабатываете SaaS-платформу. Пользователи регистрируются, создают проекты, загружают документы и работают с личными данными. Frontend написан на JavaScript, Backend — на Python с FastAPI, а информация хранится в PostgreSQL. После реализации регистрации возникает следующий вопрос: как поддерживать авторизованное состояние пользователя?

Можно выдавать JWT-токен после входа. Можно создать серверную сессию и сохранить её идентификатор в cookie. Оба варианта широко используются в современной веб-разработке. Но JWT или Session Cookies — что лучше выбрать для веб-приложения?

На первый взгляд JWT кажется более современным решением. Однако у серверных сессий есть преимущества, особенно когда требуется управлять активными устройствами, завершать сеансы и быстро отзывать доступ. Разберём, как работают оба подхода, чем они отличаются и как реализовать безопасную аутентификацию в Full Stack приложении.

Почему правильная аутентификация важна

Аутентификация отвечает на вопрос: кто выполняет запрос? Авторизация отвечает на другой вопрос: имеет ли этот пользователь право выполнить конкретное действие? Например, пользователь вошёл в личный кабинет и получил доступ к своему профилю. Но это не означает, что он должен иметь возможность открыть документы другого пользователя. Аутентификация и авторизация в веб-приложении должны работать совместно.

Даже правильно реализованный механизм входа не заменяет проверку разрешений на Backend. Ошибки в этой области могут приводить к краже сессий, несанкционированному доступу и компрометации пользовательских данных.

Что такое JWT

JWT расшифровывается как JSON Web Token. Это стандартизированный формат токена, который позволяет передавать утверждения между участниками системы. В веб-разработке JWT часто используется для подтверждения того, что пользователь прошёл аутентификацию. JWT авторизация в веб-приложении обычно предполагает выдачу подписанного токена после успешного входа. Токен содержит информацию, необходимую серверу для его проверки.

При этом JWT — не самостоятельная система управления пользователями. Он не заменяет хранение аккаунтов, проверку паролей или контроль разрешений.

Из чего состоит JWT

Подписанный JWT обычно включает три части: Header — заголовок с информацией об алгоритме подписи. Payload — утверждения, например идентификатор пользователя и срок действия. Signature — криптографическая подпись. Структура выглядит так: header.payload.signature Части токена кодируются с использованием Base64URL. Важно понимать, что обычный подписанный JWT не шифрует содержимое.

Поэтому безопасность JWT-токенов требует не помещать в Payload пароли, секретные ключи и другие конфиденциальные данные.

Как работает JWT аутентификация

Представим, что пользователь входит в образовательную платформу. Он отправляет email и пароль на Backend. Сервер проверяет учётные данные и создаёт JWT. Затем Frontend использует токен при обращении к защищённым API. Например: GET /api/profile Authorization: Bearer <access_token> Backend проверяет подпись, срок действия и другие обязательные утверждения токена.

Если проверки проходят успешно, приложение определяет пользователя. Принцип работы JWT аутентификации позволяет в некоторых архитектурах проверять токен без обращения к централизованному хранилищу сессий на каждом запросе. Это особенно удобно для распределённых API.

Как создать JWT в Python

Для FastAPI можно использовать библиотеку PyJWT. Сначала необходимо установить зависимость: pip install PyJWT Пример создания токена:

import jwt

from datetime import datetime, timedelta, timezone SECRET_KEY = "replace-with-secure-secret" ALGORITHM = "HS256" def create_access_token(user_id: str): now = datetime.now(timezone.utc) payload = { "sub": user_id, "iat": now, "exp": now + timedelta(minutes=15), "iss": "my-app", "aud": "my-app-api", } return jwt.encode( payload, SECRET_KEY, algorithm=ALGORITHM, )

Здесь sub обозначает пользователя, iat — время выдачи, а exp — момент истечения срока действия. Значение SECRET_KEY приведено только для демонстрации. В реальном проекте необходимо использовать криптографически стойкий секрет из защищённой конфигурации. Создание JWT в Python не должно включать хранение секретного ключа непосредственно в исходном коде.

Как проверить JWT в FastAPI

После получения токена Backend должен проверить его подлинность. Пример функции:

import jwt

from jwt.exceptions import InvalidTokenError def verify_access_token(token: str): try: payload = jwt.decode( token, SECRET_KEY, algorithms=["HS256"], issuer="my-app", audience="my-app-api", options={ "require": ["exp", "iat", "sub"] }, ) return payload except InvalidTokenError: return None Сервер явно ограничивает допустимые алгоритмы и проверяет обязательные утверждения.

Проверка JWT в FastAPI должна учитывать не только подпись, но и назначение токена, срок действия и правила конкретного приложения. Нельзя считать токен безопасным только потому, что его удалось декодировать.

Что такое серверная сессия

Серверная сессия — механизм хранения состояния аутентификации на Backend. После успешного входа сервер создаёт запись сессии и связывает её с пользователем. Браузер получает случайный идентификатор сессии через cookie. При последующих запросах сервер использует этот идентификатор, чтобы найти активную сессию. Серверные сессии в веб-приложении удобны тем, что Backend сохраняет контроль над состоянием входа.

Например, администратор может завершить конкретную сессию пользователя без необходимости ждать истечения JWT.

Как работают Session Cookies

Представим, что пользователь входит в CRM. Backend проверяет пароль и создаёт запись сессии в Redis. Затем отправляет браузеру cookie: Set-Cookie: __Host-session=<random_id>; Path=/; HttpOnly; Secure; SameSite=Lax При следующих запросах браузер автоматически передаёт эту cookie, если запрос соответствует правилам её отправки. Сервер получает идентификатор и проверяет, существует ли активная сессия.

Авторизация через Session Cookies позволяет хранить чувствительную информацию на сервере, а не внутри браузерного токена. При этом идентификатор сессии должен быть криптографически случайным и непредсказуемым.

JWT и серверные сессии: основные различия

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

Где хранить JWT в браузере

Один из самых распространённых вопросов — где безопаснее хранить токен после входа пользователя. Обычно рассматривают LocalStorage, SessionStorage и HttpOnly cookies. У каждого способа есть особенности.

JWT в LocalStorage

LocalStorage позволяет сохранять данные между перезагрузками страницы. Разработчик может получить токен через JavaScript: const token = localStorage.getItem("access_token"); Это удобно, но создаёт дополнительный риск. Если злоумышленник сможет выполнить JavaScript через XSS-уязвимость, он потенциально получит доступ к токену. Хранение JWT в LocalStorage особенно рискованно для долгоживущих токенов.

Поэтому этот вариант нельзя выбирать только из-за простоты реализации.

JWT в SessionStorage

SessionStorage работает похожим образом, но данные обычно связаны с конкретной вкладкой браузера. Однако они также доступны JavaScript. Следовательно, XSS может привести к компрометации токена. SessionStorage для JWT не является полноценной защитой от XSS. Разница в жизненном цикле хранения не устраняет основной риск доступа вредоносного скрипта к данным.

JWT в HttpOnly Cookies

HttpOnly cookie недоступна для чтения через document.cookie. Это снижает риск прямой кражи токена с помощью JavaScript. Пример настройки cookie в FastAPI: from fastapi import Response def set_auth_cookie( response: Response, token: str, ): response.set_cookie( key="__Host-access", value=token, httponly=True, secure=True, samesite="lax", path="/", max_age=900, )

JWT в HttpOnly cookie может быть подходящим решением для браузерного приложения. Однако такой подход требует учитывать CSRF, поскольку браузер автоматически отправляет подходящие cookies. Кроме того, HttpOnly не устраняет все последствия XSS: вредоносный код может выполнять действия в контексте уже авторизованной страницы.

Что такое Access Token

Access Token — токен, который используется для обращения к защищённым ресурсам. Например, пользователь открывает список проектов. Frontend отправляет запрос с access token, а Backend проверяет его. Access Token в JWT аутентификации обычно имеет ограниченный срок действия. Короткий срок снижает период, в течение которого украденный токен может использоваться злоумышленником.

Но слишком короткий срок без продуманного механизма обновления может ухудшить пользовательский опыт.

Что такое Refresh Token

Refresh Token позволяет получить новый access token без повторного ввода пароля. Представим, что пользователь работает с приложением несколько часов. Access token истекает через 15 минут. Вместо повторного входа клиент обращается к endpoint обновления. Backend проверяет refresh token и выдаёт новый access token.

Refresh Token для обновления JWT помогает сочетать короткий срок действия access token с удобством длительной работы. При этом refresh token не обязан быть JWT. Он может представлять собой случайное значение, связанное с серверной записью.

Как работает ротация Refresh Token

Ротация предполагает замену refresh token после успешного обновления. Процесс выглядит следующим образом: Пользователь проходит вход ↓ Получает Access Token и Refresh Token ↓ Access Token истекает ↓ Клиент отправляет Refresh Token ↓ Backend проверяет токен ↓ Выдаёт новый Access Token и Refresh Token ↓ Старый Refresh Token становится недействительным

Ротация Refresh Token помогает уменьшить риски длительного использования украденных учётных данных. Для надёжной реализации необходимо учитывать параллельные запросы и обнаружение повторного использования отозванных токенов.

Почему JWT сложно отозвать

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

  • короткоживущие access tokens;
  • отзыв refresh tokens;
  • серверные списки отозванных токенов;
  • проверка версии пользовательской сессии;
  • централизованная проверка актуальности доступа.

Если требуется немедленный отзыв access token, полностью независимая проверка токена может оказаться недостаточной.

Как завершить серверную сессию

С серверными сессиями управление выходом обычно проще. Backend удаляет или инвалидирует запись сессии. После этого соответствующий идентификатор больше не предоставляет доступ. Logout при серверных сессиях позволяет централизованно завершать вход пользователя. Например, можно реализовать функцию «Выйти со всех устройств». Для этого сервер должен хранить информацию об активных сессиях и уметь отзывать их.

Почему важна защита от CSRF

CSRF — атака, при которой злоумышленник пытается инициировать действие через браузер уже авторизованного пользователя. Если приложение использует cookie-based аутентификацию, браузер может автоматически отправлять cookies вместе с запросами. Поэтому Backend должен проверять, что изменяющая операция инициирована допустимым образом. CSRF-защита при авторизации через cookies может включать:

  • CSRF-токены;
  • проверку заголовка Origin;
  • подходящие настройки SameSite;
  • дополнительные проверки для чувствительных операций.

Использование HttpOnly не защищает от CSRF само по себе.

Почему важна защита от XSS

XSS позволяет злоумышленнику попытаться выполнить нежелательный JavaScript в контексте страницы приложения. Если токен хранится в LocalStorage, вредоносный скрипт потенциально может прочитать его. Но даже если используется HttpOnly cookie, XSS остаётся опасным. Вредоносный код может отправлять запросы от имени пользователя через браузер. Поэтому защита JWT от XSS не ограничивается выбором места хранения.

Необходимо безопасно отображать пользовательские данные, избегать небезопасной вставки HTML и использовать дополнительные механизмы защиты.

Что быстрее: JWT или Session Cookies

Иногда JWT выбирают исключительно из-за предполагаемой производительности. Действительно, подписанный токен может проверяться локально без обращения к Redis или базе данных. Но криптографическая проверка также требует вычислительных ресурсов. Серверные сессии обычно предполагают поиск записи по идентификатору. При правильно настроенном Redis такая операция может выполняться с небольшой задержкой.

Производительность JWT и серверных сессий зависит от инфраструктуры, нагрузки и выбранной архитектуры. Поэтому нельзя утверждать, что JWT всегда быстрее. Для объективного сравнения необходимо проводить нагрузочные тесты.

Как использовать Redis для хранения сессий

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

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

JWT или Session Cookies для React

React сам по себе не требует определённого механизма аутентификации. Если React-приложение работает с собственным Backend, серверные сессии с HttpOnly cookies могут оказаться простым решением. JWT тоже подходит, особенно когда API используется несколькими типами клиентов. JWT или Session Cookies для React следует выбирать с учётом того, как устроены Backend, API и управление пользовательскими устройствами.

Не стоит выбирать JWT только потому, что приложение использует современный Frontend-фреймворк.

JWT или Session Cookies для FastAPI

FastAPI позволяет реализовать оба подхода. Для JWT можно использовать PyJWT и зависимости FastAPI для проверки токена. Для серверных сессий можно создать собственный механизм с Redis или использовать подходящие библиотеки. Аутентификация в FastAPI через JWT может быть удобна для распределённых API. Серверные сессии в FastAPI часто подходят для приложений с централизованным Backend и браузерным интерфейсом.

Главное — корректно организовать проверку пользователя, защиту cookies и отзыв доступа.

JWT в микросервисной архитектуре

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

Однако необходимо отдельно контролировать аудиторию токена, права доступа, ротацию ключей и взаимодействие между сервисами. JWT не заменяет межсервисную аутентификацию и проверку разрешений.

Почему JWT не заменяет авторизацию

Представим endpoint: DELETE /api/projects/123 Пользователь отправляет корректный JWT. Но проект 123 принадлежит другому аккаунту. Если Backend проверяет только подпись токена, пользователь может получить доступ к чужому объекту. Проверка прав доступа при JWT авторизации должна выполняться отдельно. Сервер обязан убедиться, что текущий пользователь действительно имеет право удалить указанный проект.

Это особенно важно для защиты от IDOR и BOLA.

Как управлять активными устройствами

Во многих приложениях пользователи могут входить с нескольких устройств. Например, с ноутбука, смартфона и рабочего компьютера. Иногда необходимо показать список активных входов и предоставить возможность завершить отдельную сессию. Управление пользовательскими сессиями проще реализовать, когда Backend хранит соответствующие записи.

При использовании JWT такая функциональность тоже возможна, но может потребовать дополнительного серверного состояния. Поэтому полностью stateless-архитектура подходит не для каждого продукта.

Когда лучше использовать JWT

JWT стоит рассмотреть, если приложение имеет распределённую архитектуру и нескольким сервисам необходимо независимо проверять подписанные токены. Также JWT может быть удобен для API, обслуживающего разные типы клиентов. Когда использовать JWT для авторизации — вопрос архитектурных требований, а не популярности технологии.

Если приложение не нуждается в независимой проверке токенов, JWT может добавить ненужную сложность.

Когда лучше использовать серверные сессии

Серверные сессии часто удобны для:

  • CRM-систем;
  • административных панелей;
  • корпоративных порталов;
  • образовательных платформ;
  • интернет-магазинов;
  • классических браузерных SaaS-приложений.

Когда использовать Session Cookies — прежде всего тогда, когда важны простота управления входом, быстрый отзыв доступа и централизованный контроль сессий. При этом необходимо обеспечить безопасное хранение идентификаторов и защиту от CSRF.

Частые ошибки при реализации аутентификации

Первая ошибка — хранить долгоживущий JWT в LocalStorage без оценки рисков XSS. Вторая — считать, что подписанный JWT автоматически подтверждает право доступа к любому объекту. Третья — использовать слабый секрет подписи. Четвёртая — не проверять срок действия и назначение токена. Пятая — не предусмотреть отзыв доступа. Шестая — хранить refresh tokens без защиты.

Седьмая — считать HttpOnly cookie полной защитой от XSS. Восьмая — игнорировать CSRF при cookie-based аутентификации. Девятая — использовать предсказуемые идентификаторы серверных сессий. Десятая — не регенерировать идентификатор сессии после успешного входа. Эти ошибки JWT авторизации и управления сессиями могут создавать серьёзные риски даже в небольших приложениях.

Чек-лист безопасной аутентификации

Перед запуском проекта полезно проверить основные требования. Токены: ограничен ли срок действия? Проверяются ли подпись, алгоритм и обязательные утверждения? Есть ли механизм отзыва? Cookies: используются ли HttpOnly и Secure? Правильно ли настроен SameSite? Ограничена ли область действия cookie? Backend: проверяются ли права пользователя на каждый защищённый объект? Предусмотрено ли ограничение попыток входа?

Сессии: можно ли завершить активный вход? Защищены ли идентификаторы? Настроено ли удаление истёкших записей? Инфраструктура: используется ли HTTPS? Защищены ли секреты? Ведётся ли журнал подозрительных событий? Такой чек-лист безопасности JWT и Session Cookies помогает обнаружить распространённые ошибки до публикации приложения.

FAQ

Для классического браузерного приложения серверные сессии часто проще в реализации. JWT может быть удобнее для распределённых API и архитектур с независимой проверкой токенов. Оба подхода могут быть безопасными. Результат зависит от хранения учётных данных, проверки доступа, сроков действия и механизмов отзыва. Да. HttpOnly cookie ограничивает доступ JavaScript к токену, но требует учитывать CSRF и другие угрозы.

Потому что JavaScript имеет доступ к LocalStorage. При XSS-атаке злоумышленник потенциально может получить токен. Можно использовать PyJWT для проверки подписи, допустимого алгоритма, срока действия, издателя, аудитории и других обязательных утверждений. Не всегда. Refresh token полезен для обновления короткоживущих access tokens, но требует безопасного хранения и управления отзывом.

Да. Redis часто применяется как общее хранилище сессий для нескольких экземпляров Backend-приложения. Не обязательно. JWT может сократить обращения к хранилищу, но криптографическая проверка тоже требует ресурсов. Да. JWT может быть удобен для независимой проверки подписанных утверждений несколькими сервисами, если правильно организованы доверие, проверка аудитории и управление ключами.

Обычно предусматривают отзыв refresh token и короткий срок действия access token. Для немедленного отзыва access token может потребоваться дополнительная серверная проверка.

Заключение

JWT и Session Cookies — два распространённых подхода к поддержанию аутентификации в веб-приложениях. JWT позволяет передавать подписанные утверждения и может быть полезен для распределённых API. Однако его использование требует продуманного управления сроками действия, обновлением и отзывом токенов. Серверные сессии позволяют хранить состояние входа на Backend и централизованно управлять активными устройствами.

Для Full Stack разработчика важно понимать не только технические различия, но и последствия архитектурного выбора. В браузерных приложениях с собственным Backend серверные сессии часто оказываются практичным решением. JWT стоит применять тогда, когда независимая проверка токенов действительно приносит пользу.

Главный принцип: безопасная аутентификация определяется не форматом токена, а правильным управлением доступом, хранением учётных данных и защитой пользовательских сессий.


Наверх