Аутентификация: токены и cookies

Аутентификация: токены и cookies

В уроке про cookies (6-3) мы разобрали, как HTTP хранит состояние через сессионные cookies. Теперь углубим тему: как современные веб-приложения аутентифицируют пользователей через сессии, JWT-токены и OAuth.

Два подхода: сессии и токены

В вебе есть два фундаментальных подхода к аутентификации:

Сессионная аутентификация (stateful): сервер хранит информацию о сессии (в памяти, базе данных, Redis). Клиенту выдаётся ID сессии в cookie:

Клиент → POST /login {login, password}
Сервер → проверяет, создаёт сессию { id: "abc123", userId: 42, expires: ... }
         Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Lax

Клиент → GET /api/me
         Cookie: session_id=abc123
Сервер → ищет сессию по ID, находит userId=42

Токенная аутентификация (stateless): сервер никого не хранит. После логина выдаёт подписанный токен, содержащий данные пользователя. Клиент прикрепляет токен к каждому запросу в заголовке Authorization:

Клиент → POST /login {login, password}
Сервер → проверяет, создаёт JWT: { userId: 42, exp: 1714500000 }
         подписывает секретным ключом
         возвращает: { token: "eyJhbGciOi..." }

Клиент → GET /api/me
         Authorization: Bearer eyJhbGciOi...
Сервер → проверяет подпись, извлекает userId=42

JWT: структура токена

JWT (JSON Web Token) — самый популярный формат токенов. Это три части, разделённые точкой, каждая в base64:

eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjQyLCJleHAiOjE3MTQ1MDAwMDB9.abc123signature

Часть 1 (Header):    {"alg":"HS256","typ":"JWT"}
Часть 2 (Payload):   {"userId":42,"exp":1714500000}
Часть 3 (Signature): HMAC-SHA256(Часть1 + "." + Часть2, secret)

Подпись гарантирует, что токен не подделан. Проверить подпись может любой, у кого есть секретный ключ. Изменить данные в payload без знания ключа невозможно — подпись не сойдётся.

Важно: JWT не шифрует данные (по умолчанию). Payload в base64 — это не шифрование, это кодирование. Любой может раскодировать payload и прочитать содержимое. Поэтому в JWT не кладут пароли, email'ы и другие чувствительные данные. Только ID и технические флаги.

Сессии или токены: что выбрать

ХарактеристикаСессии (cookies)JWT-токены
Где хранить на клиентеCookie (автоматически)localStorage / память JS
МасштабированиеНужен общий сторадж для сессий (Redis) или sticky sessionsБез состояния — любой сервер обрабатывает
ОтзывУдалить сессию на сервере — мгновенноТокен живёт до истечения (нет встроенного отзыва без чёрного списка)
Безопасность XSSHttpOnly cookie не видна JSТокен в localStorage доступен JS (уязвим к XSS)
Безопасность CSRFУязвим (нужны SameSite + токен)Не уязвим (токен не отправляется автоматически)
Мобильные приложенияСложнее (cookie не нативные)Естественно (заголовок Authorization)

Общее правило: для веб-приложений с серверным рендерингом — сессии (cookie + HttpOnly). Для SPA + мобильные клиенты — JWT (заголовок Authorization). Многие системы комбинируют оба подхода.

OAuth 2.0 и сторонний вход

OAuth 2.0 — протокол делегирования доступа. Это когда сайт говорит «войти через Google»:

  1. Пользователь на example.com нажимает «Войти через Google».
  2. Браузер перенаправляется на accounts.google.com/o/oauth2/auth.
  3. Пользователь разрешает example.com доступ к профилю.
  4. Google выдаёт временный код (authorization code).
  5. Сервер example.com обменивает код на access-токен, обращаясь к Google API напрямую.
  6. example.com создаёт свою сессию/JWT для пользователя.

OAuth 2.0 не про аутентификацию сам по себе — это протокол авторизации (доступа). Но на практике его используют для аутентификации через социальные сети (Social Login). OpenID Connect (OIDC) — надстройка над OAuth 2.0, стандартизирующая именно аутентификацию.

Где хранить токен на клиенте

Это горячая тема в безопасности фронтенда:

СпособПлюсМинус
localStorageПростой, не отправляется автоматическиУязвим к XSS (злоумышленник читает через JS)
HttpOnly cookieНе видна JS (защита от XSS)Уязвима к CSRF (отправляется автоматически)
Память JS (переменная)Наиболее безопаснаТеряется при перезагрузке страницы

Компромиссный подход для SPA: access-токен в памяти (или localStorage), refresh-токен в HttpOnly Secure SameSite cookie. Access-токен короткоживущий (5–15 минут), refresh-токен позволяет получить новый без повторного ввода пароля.

Проверь себя

  1. Чем JWT-токен принципиально отличается от сессионной cookie?
  2. Можно ли подделать JWT, если знаешь payload, но не знаешь секретный ключ?
  3. Почему хранение токена в localStorage опасно?
<details> <summary>Ответы</summary>
  1. Сессионная cookie содержит только ID сессии — сервер ищет данные по ID (stateful). JWT содержит данные внутри себя и подписан — сервер проверяет подпись, не обращаясь к хранилищу (stateless).
  2. Нет. Любое изменение payload изменит хэш, и подпись не сойдётся. Сервер обнаружит несовпадение и отвергнет токен.
  3. Любой JavaScript на странице имеет доступ к localStorage. При успешном XSS злоумышленник может прочитать токен и отправить на свой сервер — доступ к аккаунту потерян.
</details>

Что унести с урока

  • Сессионные cookies (stateful) — сессия на сервере, ID в HttpOnly cookie. JWT (stateless) — данные в подписанном токене.
  • JWT не шифрует по умолчанию — не класть чувствительные данные в payload.
  • Компромисс: access-токен короткоживущий, refresh-токен в HttpOnly cookie.
  • OAuth 2.0 + OIDC для социального входа — стандарт для делегирования аутентификации.

В последнем уроке модуля соберём ключевые security-заголовки — чеклист для сервера, который должен быть на каждом production-сайте.

Попробуйте интерактивную версию

Практические задачи, квизы и AI-наставник — бесплатный старт без карты

Перейти к практике