Аутентификация

Базовые понятия
- Аутентификация отвечает на вопрос «кто это?» и подтверждает личность пользователя (пароль, токен, WebAuthn).
- Авторизация отвечает на вопрос «что разрешено?» — проверяет права уже известного пользователя.
- Учёт (accounting/auditing) фиксирует действия пользователя: когда зашёл, что сделал, какие данные изменил.
Схема входа
- Клиент отправляет доказательства личности (логин/пароль, OTP, подпись и т. п.).
- Сервер проверяет их и выдаёт «доказательство входа» (сессия, access token).
- Клиент прикладывает это доказательство к каждому запросу, пока оно не протухло/не отозвано.
Ниже — основные способы хранить такое доказательство.
Сессионная аутентификация
/login→ сервер создаёт запись в хранилище (Redis, БД) с ID сессии и данными пользователя.- ← браузер получает cookie с
sessionId. - Клиент отправляет куку на каждый запрос → сервер находит сессию и восстанавливает пользователя.
Плюсы:
- легко отзываться (удалить запись в хранилище) — пригодится при logout со всех устройств.
- можно хранить дополнительные данные (роль, timeouts, CSRF-токены).
Минусы:
- требуется отдельное хранилище (например, таблица в БД или Redis), за которым нужно отдельно следить.
- браузер сам прикладывает куки → уязвимость к CSRF без доп. мер.
Обязательные флаги cookie: HttpOnly, SameSite, Secure. Подробности — в конспекте Сеть.
JWT (JSON Web Token)
JWT — компактный, URL-safe токен c тремя частями header.payload.signature, подписанный секретом/SSH-ключом. Подписанный payload содержит поля с информацией о сессии: sub, exp, role, aud и др.
Как работает
/api/login→ сервер проверяет пользователя и подписывает JWT c коротким TTL (15–60 мин).- Клиент сохраняет токен (обычно в
localStorageилиHttpOnlycookie). - Все запросы → отправлять токен в заголовке
Authorization: Bearer <jwt>. - Сервер проверяет подпись и срок действия; при успехе восстанавливает контекст пользователя.
Плюсы:
- Stateless — не нужно централизованное хранилище (сервер не хранит сессию).
- Можно работать с несколькими сервисами: любой сервис с секретом проверит токен. (Напр. если есть несколько бэкендов и в случае JWT не нужно иметь коллекцию сессий в БД или отдельный бэкенд централизованный для авторизации)
Минусы:
- Отозвать токен заранее сложно → приходится хранить black list токенов или использовать короткий TTL.
- Размер токена растёт по мере добавления полей в payload → влияет на размер заголовков.
- Хранение в
localStorageуязвимо к XSS — см. раздел XSS. Поэтому нужно хранить в cookie.
Refresh tokens и ротация
- Access token делаем короткоживущим, а refresh token — долгоживущим и храним только в
HttpOnlycookie. - При
401клиент обращается к/refresh→ сервер проверяет refresh token, выдаёт новую пару и, по желанию, ревоукит старый. - Важно хранить refresh tokens серверно (whitelist) и делать ротацию (меняем и access, и refresh при каждом обновлении).
OAuth 2.0 и OpenID Connect
Используем, когда нужен вход через сторонний провайдер (Google, GitHub) или доступ к API от имени пользователя.
- OAuth 2.0 — протокол делегирования доступа.
- Authorization Code Flow (с PKCE для SPA) — безопасный способ получить access token через редирект и одноразовый код.
- Scopes описывают, что можно делать:
read:user,repo,openid email. - OpenID Connect — надстройка над OAuth 2.0, которая даёт id_token (JWT) с данными пользователя. Используем для SSO (Single Sign-on - одна авторизация для разных сайтов, вроде YouTube и Google).
Важно валидировать redirect_uri, state и nonce, хранить client secret на сервере и регулярно вращать ключи.
Авторизация и контроль доступа
RBAC (role-based)
Наборы прав привязываем к ролям (admin, editor, viewer).
В БД в коллекции users есть поле role.
Наиболее простая реализация авторизации, но и наименее гибкая.
ABAC (attribute-based)
Когда RBAC не хватает и приходится добавлять доп. условия к ролям, напр. if (user.role === 'admin' && user.id === comment.authorId)
Тогда удобно их вынести в централизованную функцию-хелпер.
// policy.js export function can(user, action, resource) { switch (action) { case "addComment": return user.role === 'admin'; case "editComment": return user.role === "admin" && user.id === comment.authorId; default: return false; } }
PBAC / Policy-based
Правила описываем декларативно: напр. в JSON и используем движок (библиотеку напр. Casbin) для интерпретации этих правил на фронте и бэке.
// policies/post.json
{ "version": 1, "policies": [ { "effect": "allow", "role": "editor", "action": "posts.edit", "resource": "post" }, { "effect": "allow", "role": "author", "action": "posts.edit", "resource": "post", "condition": "resource.authorId == user.id" }, { "effect": "deny", "role": "*", "action": "posts.delete", "resource": "post" } ] }
Политики можно менять без релиза — достаточно обновить файл или конфигурацию.
Scope-based
Классический подход для OAuth и API. JWT-токен содержит перечень разрешений (scopes), определяющих, какие действия пользователь может выполнять. Бэкенд использует эти скоупы, чтобы решать, имеет ли клиент право вызывать конкретный эндпоинт. Фронтенд может декодировать токен и применять эти же разрешения для управления интерфейсом (например, скрывать недоступные кнопки или страницы).
{ "sub": "123", "exp": 1736342261, "scope": "users:read orders:write" }
Практика
- правила и роли храним в БД/конфигурации, кэшируем и инвалидируем при обновлении;
- frontend отдаём минимальный список разрешений (
['posts.edit']), UI скрывает недоступные действия; - backend всегда делает финальную проверку (нельзя доверять только фронту);
- в микросервисах выносим общую библиотеку или отдельный сервис авторизации, чтобы все сервисы проверяли права одинаково.
Хранение секретов и валидация
- Пароли хешируем (bcrypt/argon2/scrypt), используем соль и параметризацию (work factor).
- При регистрации и смене пароля — rate limiting + капча/механизм задержки.
- Верифицируем email/телефон, чтобы уменьшить количество одноразовых аккаунтов.
- Логируйте попытки входа и храните аудит в неизменяемом хранилище.
Многофакторная аутентификация (MFA)
Второй фактор — что-то, что у пользователя есть (телефон, ключ), является (биометрия) или знает (PIN).
- TOTP (Google Authenticator): сервер хранит секрет, выдаёт QR-код, проверки через
code = totp(secret, timestamp). - FIDO2/WebAuthn: криптография с ключом, работает без паролей (passkeys). Часто реализуют через
navigator.credentials. - Backup codes — одноразовая матрица для восстановления.
На фронте — обязательное UX: предупреждение о невозможности входа без второго фактора и маршруты для восстановления.
Типовые угрозы и защита
- CSRF — защищаемся
SameSite+ CSRF-токены, см. подробности в конспекте Сеть. - XSS — токены в памяти или
HttpOnlycookie, проверяем инпуты, см. раздел XSS. - MITM и перехват трафика — используем HTTPS/HSTS и защищённые каналы, подробнее в Сеть.
- Brute-force (атака, когда спамят логин/пароль пытаясь подобрать) — rate limiting, блокировка IP, reCAPTCHA.
- Credential stuffing — мониторим утечки, внедряем защиту на основе аномалий (невозможные путешествия, новые устройства).
- Session fixation — регенерация идентификаторов сессии напр. после смены прав (роли).
Если приложение SPA:
- избегаем хранения access token в
localStorage→ лучшеHttpOnlycookie +SameSite=strict. - при необходимости работать с небезопасными запросами — используем
fetchсcredentials: 'include'и настройками CORS (см. CORS).