Веб-разработка

Аутентификация: JWT, OAuth, Sessions

В 2014 году Slack обнаружил, что атакующий получил токены пользователей через XSS в чате. Токены лежали в localStorage. Через два года Slack полностью перешёл на HttpOnly cookies + короткие JWT - и сэкономил на инциденте, который мог стоить как Equifax (1.4 млрд долларов).

  • **Slack (2014)**: XSS в чате + localStorage = массовая утечка токенов
  • **Equifax (2017)**: 1.4 млрд долларов штрафов за компрометацию учётных данных 147 млн пользователей
  • **OAuth 2.0 Security BCP (RFC 8252)**: Implicit Flow официально deprecated, требуется PKCE
  • **Auth0/Okta**: индустриальный стандарт - гибрид access JWT (15 мин) + refresh token в БД
  • **Chrome 80+ (2020)**: SameSite=Lax default - тихая глобальная мера против CSRF

JWT: stateless токены

JSON Web Token - это компактный self-contained формат для передачи claims между сторонами. Структура: header.payload.signature, три base64url-сегмента через точки. Сервер подписывает токен своим секретом (HS256) или приватным ключом (RS256), клиент шлёт его обратно при каждом запросе.

**Главная боль JWT:** отозвать невозможно. Если access token утёк, он работает до истечения exp. Решения: короткие TTL (5-15 мин), refresh token rotation, blocklist в Redis для досрочного отзыва.

Что обеспечивает signature в JWT?

OAuth 2.0 и OIDC

OAuth 2.0 - протокол делегирования доступа: приложение получает токен для работы с ресурсами от имени пользователя, не зная его пароля. OpenID Connect (OIDC) - надстройка над OAuth, добавляющая identity layer: сервер выдаёт id_token (JWT) с claims о пользователе.

**Authorization Code Flow + PKCE** - современный стандарт для веба и мобильных: 1. Клиент редиректит на authorize endpoint с code_challenge 2. Пользователь логинится у провайдера 3. Провайдер возвращает code на redirect_uri 4. Клиент меняет code + code_verifier на access_token + id_token на token endpoint

**Implicit flow устарел.** Запрещён в OAuth 2.1 (черновик) и в спецификации OAuth Security BCP. Использовать Authorization Code + PKCE даже для SPA.

Зачем нужен PKCE в authorization code flow?

Server sessions vs JWT

Server session - классический подход: сервер хранит session state в Redis или БД, клиент носит opaque session_id в cookie. JWT - stateless подход: state в самом токене, сервер только проверяет подпись.

**Trade-off матрица:** | Параметр | Server Session | JWT | |---|---|---| | Отзыв | мгновенный | требует blocklist | | Масштабирование | нужен shared store | stateless | | Размер на каждый запрос | 32 байта | 500-1500 байт | | Хранилище БД | требуется | не нужно | | Изменение прав | сразу | до истечения TTL |

**Гибридный подход (используют Auth0, Okta):** короткий access JWT (15 мин) + длинный refresh token в БД. Access stateless для скорости, refresh stateful для отзыва. Best of both worlds.

Команда выбирает между session-based и JWT для нового приложения. Когда JWT лучше?

Cookies: httpOnly, Secure, SameSite

Хранение токена в localStorage - известная уязвимость: любой XSS читает токен и отправляет атакующему. Стандартное решение - класть токен в cookie с тремя обязательными флагами: HttpOnly, Secure, SameSite.

**localStorage = XSS-уязвимость.** Любой `<script>` на странице (через рекламу, npm-зависимость с supply chain attack, отзывы пользователей без sanitize) может прочитать токен. HttpOnly cookie этому недоступны.

**CSRF protection в дополнение к SameSite:** double-submit cookie (CSRF-token в cookie + header, сервер сверяет) или synchronizer pattern (one-time token в форме). SameSite=lax сам по себе - первая линия защиты, но не единственная.

JWT в localStorage безопасен, потому что подписан криптографически.

Подпись защищает от подделки токена, но не от его кражи. Если XSS читает токен и отправляет атакующему - тот использует подписанный валидный токен и проходит проверку.

Yahoo, Twitter, Slack теряли пользовательские сессии через XSS + localStorage именно так. Цифровая подпись подтверждает, что данные не изменены, но не привязывает токен к конкретному пользователю или браузеру. Правильное решение - HttpOnly cookie плюс короткий TTL плюс строгий CSP.

Почему `localStorage.setItem('token', jwt)` - плохая практика?

Ключевые идеи

  • JWT - stateless токены с подписью. Self-contained: claims читаются без БД. Главная проблема: невозможно отозвать без blocklist
  • OAuth 2.0 = делегирование, OIDC = identity layer. Современный стандарт - Authorization Code Flow + PKCE. Implicit flow устарел
  • Server sessions stateful (отзыв мгновенный, нужен shared store). JWT stateless (масштабируется, но не отозвать). Гибрид: access JWT + refresh в БД
  • Cookies лучше localStorage. Обязательные флаги: HttpOnly (защита от XSS), Secure (только HTTPS), SameSite=lax (защита от CSRF)

Связанные темы

Аутентификация - сквозной слой, который влияет на API-дизайн, транспорт и хранение секретов; пересекается с предыдущими темами курса:

  • GraphQL — auth слой над API
  • REST API Design — Authorization header + Bearer токены
  • Node.js и Express — passport.js, express-session, jsonwebtoken
  • HTTP в браузере — cookies, CORS, secure flags на транспортном уровне

Вопросы для размышления

  • Когда выберете JWT, а когда server sessions? Назовите по два критерия для каждого
  • Зачем PKCE нужен SPA, если client_secret там и так нельзя хранить?
  • Опишите атаку, которая работает с JWT в localStorage и не работает с HttpOnly cookie
  • Что делать, если access token утёк, но refresh token в БД?

Связанные уроки

  • web-13 — GraphQL требует особой настройки аутентификации
  • web-15 — После auth - продвинутые паттерны безопасности
  • sec-01 — Основы security перед JWT и OAuth
  • crypto-19-hash-functions — Хеш-функции за HMAC подписями JWT
  • net-15-tcp-basics — HTTPS/TLS - обязательная основа для OAuth flow
Аутентификация: JWT, OAuth, Sessions

0

1

Войти