# Кто ты — и что тебе можно?

Экспресс-курс по аутентификации и авторизации. 22 понятия — authN и authZ, хеширование паролей, MFA, passkeys, сессии и JWT, OAuth 2.0 и OIDC, API-ключи, least privilege, RBAC, ABAC, проверка владения, IDOR и другие типичные провалы — у каждого английское название, схема, образ и карточка с примером. В конце — шпаргалка по всем понятиям на одной странице.

- **Author:** Фёдор Чищин
- **Level:** intro
- **Reading time:** ~35 минут чтения
- **Published:** 2026-06-02
- **Source:** https://fedorthinks.com/ru/learn/auth-express

---

## 1. Два вопроса, одно слово

За словом «auth» прячутся два разных вопроса. Большинство багов безопасности рождается из их путаницы — или из того, что отвечают на один и забывают про другой.

### Authentication (authN) — аутентификация: кто ты?

> Показать паспорт в аэропорту — доказать, что ты и правда тот человек, за кого себя выдаёшь.

Это понятие **Authentication (authN)**, и оно означает подтверждение личности: вход, passkey, токен, который говорит «это и правда Алиса». Обычно случается **один раз за сессию.** Ошибёшься — и самозванец входит как кто-то другой. Но подтверждение того, кто ты, не говорит ничего о том, что тебе можно, — это совсем другой вопрос.

_Diagram: AuthN превращает креденшл (пароль, passkey, токен) в личность — и отвечает только на вопрос «кто это»._

#### Authentication (authN) (аутентификация)

- **Что это:** Проверка, что пользователь — тот, за кого себя выдаёт.
- **Как работает:** Пользователь предъявляет креденшл (пароль, passkey, токен), система его проверяет и устанавливает личность.
- **Пример:** Логин по email и паролю, «это Алиса».
- **Подвох:** Знать, кто человек, — ещё не значит знать, что ему можно.
- **Связи:** Вход для **Authorization**; способы — **Password Hashing**, **MFA**, **Passkeys**, **OIDC**.

### Authorization (authZ) — авторизация: что тебе можно?

> Карта-ключ от отеля — она ничего не доказывает о твоём имени, но открывает твой номер, а не чужие, и не кабинет управляющего.

Это понятие **Authorization (authZ)**: решение, что личности разрешено делать — читать это, править то, никогда не трогать админку. В отличие от authN, её надо проверять **на каждый запрос**, потому что один залогиненный пользователь может трогать сотню разных вещей. **Знать, кто человек, — это не разрешение делать что-то конкретное.**

_Diagram: AuthZ берёт «кто», «что делает» и «над чем» — и на каждый запрос выносит решение: разрешить или отказать._

#### Authorization (authZ) (авторизация)

- **Что это:** Решение, что этой личности можно делать с этим ресурсом.
- **Как работает:** На каждый запрос: кто + действие + ресурс → правило → разрешить или отказать.
- **Пример:** Алиса-редактор может править документ #42, но не может открыть админку.
- **Подвох:** Проверить один раз и забыть. Проверять надо **каждый запрос**, на сервере.
- **Связи:** Модели — **Least Privilege**, **RBAC**, **ABAC**, **Ownership Check**; провал — **Broken Access Control**.

### Два слоя — никогда не схлопывай их

> Клуб: вышибала проверяет ID на входе один раз, а бармен всё равно проверяет твой возраст каждый раз, когда ты заказываешь. Две проверки, два момента.

Классическая ошибка — слить эти двое: подтвердить личность один раз и доверять этому навсегда или запихнуть все права в токен входа и больше не перепроверять. **AuthN происходит на входной двери; authZ — на каждой двери внутри.** Держи их раздельно и проверяй вторую постоянно.

_Diagram: AuthN — одна входная дверь. AuthZ — проверка у каждой внутренней двери, то есть на каждом запросе._

### Don't Roll Your Own Auth — никогда не делай свою

> Ты не варишь себе банковский сейф из листового железа в гараже — ты покупаешь тот, что построили и испытали люди, которые больше ничем не занимаются.

Это практика **Don't Roll Your Own Auth**. Личность — это фундамент, на котором стоит всё остальное, и ошибиться в ней тонко до жути легко. **Используй проверенные стандарты и библиотеки** — устоявшиеся протоколы, обкатанную auth-библиотеку или провайдера, — а не хитрую схему, которую ты придумал. История безопасности вымощена самодельным auth, который выглядел нормально, пока вдруг не переставал.

#### Don't Roll Your Own Auth (не изобретай свой auth)

- **Проблема:** Самодельный auth выглядит нормально, пока его не сломают.
- **Идея:** Брать проверенные протоколы, библиотеки и провайдеров.
- **Пример:** Вместо своей схемы токенов — OIDC и auth из фреймворка.
- **Цена:** Надо разобраться в чужом инструменте и правильно его подключить.
- **Когда брать:** Почти всегда.
- **Связи:** Ведёт к **Auth Provider**; стандарты — **OAuth 2.0**, **OIDC**.

### Карта: 22 понятия в шести группах

> Связка ключей: сначала видишь всю связку, потом узнаёшь, какой ключ от какой двери.

Понятия курса делятся на шесть групп. Каждая отвечает на свой вопрос: что такое два «auth», как доказать личность, как остаться залогиненным, как дать доступ без пароля, как решать про права и как всё это обычно ломается.

_Diagram: Шесть групп понятий — по ним идут главы 1–6. Глава 7 — как выбрать и шпаргалка._

У каждого понятия дальше одно и то же: **английское название** (это термин, по нему ищут в документации), образ из жизни, **схема** и **карточка** с полями. В конце — шпаргалка со всеми понятиями.

> **Прежде чем решать про auth:** «Это про подтверждение того, кто человек, — или про решение, что ему можно? Это не один и тот же замок.»

> Аутентификация — это дверь. Авторизация — каждая дверь внутри. Пропусти вторую — и первая ничего не значит.

## 2. Подтвердить, кто ты

Аутентификация сводится к доказательству, что ты — это ты. Факторы тянутся от слабого, но универсального пароля до современного, устойчивого к фишингу passkey.

### Password Hashing — пароли: слабый дефолт

> Секретное слово, известное тебе и швейцару, — нормально, пока его не подслушали, не угадали или ты не используешь одно и то же везде.

Пароли универсальны и при этом — слабейший общий фактор: их переиспользуют, угадывают, выуживают фишингом. Если без них никак, незыблемое правило — **никогда не хранить их текстом**. Храни медленный, солёный **хеш** (Argon2, bcrypt) — это и есть **Password Hashing**, — чтобы украденная база не выдала всем чужие пароли. (Подробнее в главе про ошибки.)

_Diagram: Пароль + случайная соль → медленный хеш → в базе только хеш. Украденная база не отдаёт пароли._

#### Password Hashing (хеширование паролей)

- **Проблема:** Пароли в базе текстом: одна утечка — и все пароли у атакующего.
- **Идея:** Хранить не пароль, а медленный хеш с солью (**salt**) — своей для каждого пользователя.
- **Пример:** При входе считаешь Argon2/bcrypt от введённого пароля и сравниваешь.
- **Цена:** Вход чуть медленнее — и это нарочно.
- **Когда брать:** Всегда, если хранишь пароли.
- **Связи:** Анти-пример — **Plaintext / Fast Hash**; сверху добавь **MFA**.

### MFA — многофакторка: что-то знаешь, имеешь, есть

> Банковской ячейке нужен твой ключ и ключ банка. Один сам по себе не открывает ничего.

**MFA (Multi-Factor Authentication)**, или двухфакторка, требует второе доказательство сверх пароля — **что-то, что у тебя есть** (код из приложения, аппаратный ключ) или **что-то, чем ты являешься** (отпечаток). Тогда даже украденный пароль сам по себе бесполезен. Код из приложения-аутентификатора (**TOTP**) надёжен; SMS-коды слабы (перехватываемы), но лучше, чем ничего. **MFA — это самый большой и дешёвый выигрыш в безопасности, который можно добавить.**

_Diagram: Три вида факторов: знаешь, имеешь, являешься. Для входа нужны минимум два разных._

#### MFA (Multi-Factor Authentication, 2FA)

- **Проблема:** Один украденный пароль = полный вход в аккаунт.
- **Идея:** Требовать второй фактор другого типа: «имеешь» или «являешься».
- **Пример:** Пароль + шестизначный TOTP-код из приложения-аутентификатора.
- **Цена:** Лишний шаг при входе; SMS-коды можно перехватить.
- **Когда брать:** Везде, где это важно; на админских аккаунтах — обязательно.
- **Связи:** Укрепляет **Password Hashing**; **Passkeys** — следующий шаг.

### Passkeys — будущее без паролей

> Ключ, который работает только у той одной двери, под которую был выточен, — подделай сайт, сфоткай замок, и ключ просто не повернётся.

**Passkeys** (на основе стандарта **WebAuthn**) заменяют пароли криптографической парой ключей, привязанной к твоему устройству и настоящему сайту, так что нечего фишить, переиспользовать или сливать — и Apple, Google и Microsoft уже делают их дефолтом. Приватный ключ никогда не покидает устройство; сайт видит лишь подпись. **Вот куда движется аутентификация: красть нечего.**

_Diagram: Сайт шлёт вызов, устройство отвечает подписью. Фишинговый сайт с другим адресом подписи не получит._

#### Passkeys (WebAuthn, вход без пароля)

- **Проблема:** Пароли фишат, переиспользуют и сливают.
- **Идея:** Пара ключей: приватный остаётся на устройстве, у сайта — публичный. Вход — это подпись.
- **Пример:** Вход по отпечатку или Face ID вместо ввода пароля.
- **Цена:** Нужна поддержка устройств и запасной путь восстановления.
- **Когда брать:** Как основной вход там, где пользователи на современных устройствах.
- **Связи:** Заменяет **Password Hashing**; сам по себе сильнее **MFA** по SMS.

> **Вопрос про аутентификацию:** «Если бы этот единственный фактор украли, смог бы кто-то войти как пользователь — и какая у меня вторая линия обороны?»

> Пароль — это секрет, который можно потерять. Passkey — это доказательство, которое нельзя. Добавь второй фактор в любом случае.

## 3. Оставаться залогиненным

HTTP забывает тебя между запросами, так что после входа системе нужен способ продолжать тебя узнавать. Два подхода с реальным разменом.

### Stateless HTTP — у HTTP нет памяти

> Магазин, где продавец забывает тебя, едва ты отвернулся, — поэтому нужен чек, чтобы на следующем шаге доказать, что ты уже заплатил.

HTTP — **stateless** (без состояния): каждый запрос независим, сервер не помнит, что ты вошёл секунду назад. Поэтому после аутентификации тебе вручают нечто — куку или токен, — что ты шлёшь с каждым запросом, говоря «это всё ещё я». Всё ниже — два способа это сделать. (Это та самая безсостоятельность из курса про протоколы.)

_Diagram: Сервер не помнит прошлых запросов, поэтому каждый запрос несёт с собой куку или токен._

#### Stateless HTTP (HTTP без состояния)

- **Что это:** Сервер не помнит предыдущие запросы клиента.
- **Следствие:** После входа нужен «пропуск», который едет с каждым запросом.
- **Пример:** Браузер сам прикладывает куку к каждому запросу на этот сайт.
- **Связи:** Два ответа — **Session** и **JWT**.

### Session — сессии: сервер помнит

> Гардероб — у тебя на руках маленький номерок, а вся реальная информация лежит за стойкой под этим номером.

При **сессиях (sessions)** сервер хранит, кто ты, и вручает тебе куку, в которой лежит лишь случайный **session id**. На каждый запрос он этот id ищет. Сервер — источник правды, так что выйти или забанить кого-то — мгновенно: просто удали сессию. Цена в том, что серверу надо хранить это состояние и искать в нём. **Правильный дефолт для большинства веб-приложений.**

_Diagram: В куке только id. Сервер ищет его в хранилище сессий. Удалил сессию — пользователь вышел сразу._

#### Session (серверная сессия)

- **Проблема:** HTTP не помнит, кто ты, между запросами.
- **Идея:** Сервер хранит данные входа, клиент держит только случайный session id.
- **Пример:** Кука `sid=8f3a…` → в хранилище `8f3a → Алиса`.
- **Цена:** Серверу нужно хранилище и поиск на каждый запрос.
- **Когда брать:** Обычное веб-приложение. Это дефолт.
- **Связи:** Альтернатива — **JWT**; id хранится в **httpOnly Cookie**; отзыв — мгновенный.

### JWT — токен несёт правду

> Фестивальный браслет, на котором уже напечатан и запаян от подделки твой уровень доступа, — охранник проверяет пломбу, а не список гостей.

**JWT (JSON Web Token)** — это подписанный токен на руках у клиента, который _содержит_ личность и права (**claims**); сервер просто проверяет подпись, без поиска. Это делает его безсостоятельным и лёгким для масштабирования по сервисам — но **тяжёлым для отзыва:** утёкший токен валиден, пока не истечёт. Поэтому держи срок жизни коротким и сочетай с **refresh token**. **Отлично для API и микросервисов; избыточно для простого веб-приложения.**

_Diagram: JWT = header.payload.signature. API проверяет подпись без базы. Утёкший токен живёт до exp — отсюда короткий срок и refresh-токен._

#### JWT (JSON Web Token)

- **Проблема:** Много сервисов, и каждому лезть в общее хранилище сессий — дорого.
- **Идея:** Положить личность и права в сам токен и подписать его.
- **Пример:** Payload `sub: alice, role: editor, exp: …`; каждый сервис проверяет подпись сам.
- **Цена:** Трудно отозвать: утёкший токен валиден до истечения.
- **Когда брать:** API и микросервисы — короткоживущий JWT + **refresh token**.
- **Связи:** Альтернатива **Session**; угроза — **Token Leakage**.

### httpOnly Cookie — где ты его хранишь, важно

> Ключ в запертом ящике против ключа, приклеенного к входной двери, — ключ тот же, безопасность совсем разная.

Токену в браузере место в **httpOnly cookie** (JavaScript не может её прочитать), а не в `localStorage`, откуда любой внедрённый скрипт его украдёт. Хранилище — часть безопасности: ошибёшься — и один **XSS**-баг отдаёт атакующему все сессии. **Как ты переносишь и хранишь креденшл, не менее важно, чем сам креденшл.**

_Diagram: Внедрённый скрипт (XSS) читает localStorage, но не может прочитать httpOnly-куку._

#### httpOnly Cookie (кука, закрытая от JavaScript)

- **Проблема:** Токен в `localStorage` крадёт любой внедрённый скрипт.
- **Идея:** Хранить токен в куке с флагом `httpOnly` — JS её не видит.
- **Пример:** `Set-Cookie: sid=…; HttpOnly`.
- **Цена:** Почти никакой — это правильный дефолт для браузера.
- **Связи:** Хранит **Session** id или **JWT**; защищает от **Token Leakage**.

> **Вопрос про сессии:** «Мне нужны мгновенный отзыв и простота (сессии) — или безсостоятельный масштаб по сервисам (короткоживущие JWT)?»

> Сессии: сервер помнит и может забыть тебя мгновенно. JWT: помнит токен, и забрать его назад непросто.

## 4. Делегированный доступ

Часто ты вообще не хочешь иметь дело с паролем — ты хочешь, чтобы пользователи входили под уже имеющимся аккаунтом или чтобы один сервис действовал за другой. Для этого и есть OAuth2 и OIDC.

### OAuth 2.0 — дать доступ, не делясь паролем

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

Это протокол **OAuth 2.0**. Он позволяет пользователю выдать твоему приложению ограниченный доступ к его аккаунту в другом сервисе — твоё приложение читает его Google Calendar — **ни разу не дав тебе его пароль от Google.** Другой сервис вручает твоему приложению ограниченный по области (**scope**) **access token** («читать календарь, больше ничего»). Это про делегированную _авторизацию_, а не про личность.

_Diagram: Пользователь входит у Google и даёт согласие. Приложение получает токен с узкой областью и ходит с ним в API. Пароль оно не видит._

#### OAuth 2.0 (делегированная авторизация)

- **Проблема:** Приложению нужен доступ к чужому аккаунту, но пароль давать нельзя.
- **Идея:** Сервис-владелец выдаёт приложению **access token** с узким **scope**.
- **Пример:** Твоё приложение читает Google Calendar пользователя — и только его.
- **Цена:** Протокол сложный; самому танцевать с токенами опасно.
- **Когда брать:** Действовать от имени пользователя в другом сервисе.
- **Связи:** Не говорит, кто пользователь, — для этого **OIDC** сверху.

### OIDC — OAuth2, который ещё и говорит, кто ты

> Тот же парковочный ключ, но теперь с пристёгнутым удостоверением с фото — не только «что этот ключ открывает», но и «и вот чей он».

Один OAuth2 говорит, к чему приложению можно дотянуться, а не _кто_ пользователь. **OpenID Connect (OIDC)** добавляет сверху тонкий слой личности — **ID token** с проверенными claims (это Алиса, вот её email). Именно это питает «**Войти через Google**» и **single sign-on (SSO)**. Когда нужен соц-логин или SSO, OIDC — это стандарт.

_Diagram: OIDC — тонкий слой над OAuth 2.0: к access-токену («что можно») добавляется ID-токен («кто это»)._

#### OIDC (OpenID Connect)

- **Проблема:** OAuth2 даёт доступ, но не говорит, кто пользователь.
- **Идея:** Добавить **ID token** с проверенными данными о личности.
- **Пример:** Кнопка «Войти через Google»; вход в рабочие сервисы через SSO.
- **Цена:** Зависишь от внешнего провайдера личности.
- **Когда брать:** Соц-логин и single sign-on.
- **Связи:** Построен на **OAuth 2.0**; это способ **Authentication**.

### API Key — простой машинный креденшл

> Запасной ключ, который ты даёшь доверенному подрядчику, — без имени, просто «у кого он есть, тот может войти».

**API key** — это длинная секретная строка, которая идентифицирует программу, а не человека, — для доступа «сервис — сервис» и программного доступа. Просто и эффективно, но он _и есть_ креденшл: кто его найдёт, тот внутри. Поэтому ограничивай его область, ротируй и **никогда не коммить в git и не клади в URL.** Хорош для машин; не замена настоящей личности пользователя.

_Diagram: Программа шлёт ключ в заголовке, API проверяет ключ и его область. Ключ не в git, не в URL, регулярно меняется._

#### API Key (API-ключ)

- **Проблема:** Программе нужно входить в API без человека и без логина.
- **Идея:** Длинный секрет, который опознаёт программу.
- **Пример:** Бэкенд вызывает платёжный API с ключом в заголовке.
- **Цена:** Ключ и есть доступ: нашёл — вошёл.
- **Когда брать:** «Машина — машина». Не для входа людей.
- **Связи:** Ограничивай по **Least Privilege**; главная угроза — **Token Leakage**.

> **Вопрос про делегирование:** «Мне нужно действовать от имени пользователя где-то ещё (OAuth2), узнать, кто он, через провайдера (OIDC), или просто опознать машину (API-ключ)?»

> OAuth2 одалживает доступ без пароля. OIDC добавляет «и вот кто это». Используй стандарт — не изобретай свой танец с токенами.

## 5. Что тебе можно делать

Когда ты знаешь, кто человек, ты решаешь, что ему можно трогать. Модели тянутся от грубых ролей до тонкой политики — и правило под всеми ними одно: deny by default.

### Deny by Default / Least Privilege — запрещено всё, что не разрешено

> Бейдж нового сотрудника, который не открывает ничего, пока каждую дверь явно не разрешат, — а не открывает всё, пока кто-то не вспомнит запереть пару.

Фундамент авторизации — два правила. **Deny by default:** всё запрещено, пока явно не разрешено. **Least privilege (минимум привилегий):** каждая личность получает **минимум** доступа, который ей нужен, не больше. Системы «по умолчанию открыто» текут — кто-то всегда забывает ограничить новый эндпоинт. Системы «по умолчанию закрыто» падают безопасно. **Начинай с «нет» и выдавай осознанно.**

_Diagram: Есть явное разрешение — пускаем, и только на нужный минимум. Нет или непонятно — отказ._

#### Least Privilege (deny by default, минимум привилегий)

- **Проблема:** Новый эндпоинт забыли закрыть — и он открыт всем.
- **Идея:** По умолчанию «нет»; выдавать только нужный минимум.
- **Пример:** Новый сотрудник получает доступ только к своему проекту.
- **Цена:** Чаще приходится явно выдавать права.
- **Когда брать:** Всегда — это основа любой модели прав.
- **Связи:** Фундамент для **RBAC**, **ABAC**, **Ownership Check**.

### RBAC — права по роли

> Театр, где тип билета — партер, бельэтаж, бэкстейдж — решает, куда тебе можно, не называя тебя лично.

**RBAC (Role-Based Access Control)** группирует права в роли — admin, editor, viewer — и назначает пользователям роли. Просто, читаемо и достаточно для большинства приложений: «редакторы могут публиковать, зрители нет». Становится грубо, когда правила зависят от контекста или от конкретной записи, — тут на сцену выходят следующие модели.

_Diagram: Пользователь → роль → набор прав. Права выдаются ролям, а не людям лично._

#### RBAC (Role-Based Access Control)

- **Проблема:** Выдавать права каждому человеку по отдельности — хаос.
- **Идея:** Права — ролям, роли — пользователям.
- **Пример:** «Редакторы могут публиковать, зрители — только читать».
- **Цена:** Грубо, когда правило зависит от контекста или конкретной записи.
- **Когда брать:** Большинство приложений.
- **Связи:** Тоньше — **ABAC**; всегда дополняй **Ownership Check**.

### ABAC — права по политике

> Правило вместо списка — «менеджеры могут одобрять расходы до 10k, в рабочие часы, с корпоративного устройства» — вычисляемое заново каждый раз.

**ABAC (Attribute-Based Access Control)** решает по атрибутам пользователя, ресурса и контекста — роль _и_ сумма _и_ время _и_ устройство. Куда гибче фиксированных ролей и куда сложнее. Сигнал, что пора: ты навешиваешь больше пары кастомных условий `if` на свои проверки ролей.

_Diagram: Атрибуты пользователя, ресурса и контекста идут в политику, которая выносит решение заново на каждый запрос._

#### ABAC (Attribute-Based Access Control)

- **Проблема:** Права зависят не только от роли, но и от суммы, времени, устройства.
- **Идея:** Правило-политика над атрибутами пользователя, ресурса и контекста.
- **Пример:** Менеджер одобряет расход до 10k, в рабочие часы, с рабочего ноутбука.
- **Цена:** Гораздо сложнее писать, читать и тестировать.
- **Когда брать:** Когда к ролям прилипает больше пары кастомных `if`.
- **Связи:** Развитие **RBAC**; стоит на **Least Privilege**.

### Ownership Check — можешь ли ты трогать эту запись?

> У тебя есть действующий читательский билет (роль), но это не даёт тебе читать заметки, нацарапанные в чужой взятой на руки книге.

**Ownership check** (проверка владения, или авторизация на уровне объекта) — самая пропускаемая проверка: даже валидный «редактор» должен править только **свои** документы, а не все подряд. Роль говорит «редакторы могут править»; тебе всё равно надо проверить, что этот редактор владеет _этой_ строкой. Забыть это — самый частый баг контроля доступа: поменял id в URL — и ты в чужих данных. **Всегда проверяй объект, а не только роль.**

_Diagram: Две проверки: роль («редактор?») и владение («этот документ — его?»). Пускаем, только если прошли обе._

#### Ownership Check (проверка владения, object-level authZ)

- **Проблема:** Роль разрешает «править документы» — и человек правит чужие.
- **Идея:** Кроме роли проверять, что этот объект принадлежит этому пользователю.
- **Пример:** Перед `PUT /docs/42` проверить: владелец документа 42 — текущий пользователь.
- **Цена:** Лишняя проверка на каждый доступ к объекту.
- **Когда брать:** Всегда, когда у данных есть владелец.
- **Связи:** Дополняет **RBAC**/**ABAC**; без неё — **IDOR**.

> **Вопрос про права:** «Проверил ли я не только роль, но и что именно этому пользователю можно трогать именно эту вещь?»

> Deny by default. Выдавай минимум. И всегда спрашивай не только «какая роль», но и «чья запись».

## 6. Как это ломается

Auth ломается горсткой хорошо известных способов — одних и тех же, снова и снова, в приложении за приложением. Знать их — это половина защиты.

### Broken Access Control / IDOR — забыл проверить

> Здание, где входная дверь под охраной, но каждая внутренняя — нараспашку: раз уж ты внутри, можешь ходить где угодно.

**Broken Access Control** — уязвимость номер один в вебе: эндпоинт аутентифицирует пользователя, но **забывает проверить, что ему авторизовано** то, что он попросил. Её частейшая форма — **IDOR (Insecure Direct Object Reference)**: меняешь `/orders/123` на `/orders/124` и видишь чужой заказ, потому что сервер ни разу не проверил владение. Утечки миллионов записей восходят ровно к этому. **Проверяй authZ на каждый запрос, на сервере, против реального объекта.**

_Diagram: Боб вошёл честно, но сервер не проверяет владение — поменял id в URL и видишь чужой заказ._

#### Broken Access Control / IDOR (сломанный контроль доступа)

- **Что это:** Пользователь аутентифицирован, но права на объект не проверены.
- **Как выглядит:** `/orders/123` → `/orders/124` — и виден чужой заказ.
- **Чем грозит:** Утечки миллионов записей.
- **Как чинить:** authZ на каждый запрос, на сервере: `WHERE id = 124 AND owner = current_user`.
- **Связи:** Лечится **Ownership Check** и **Least Privilege**.

### Plaintext / Fast Hash — неправильное хранение паролей

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

Хранение паролей текстом (**plaintext**) — или с быстрым хешем вроде **MD5** — означает, что одна утечка базы обнажает всех, а переиспользованные пароли компрометируют ещё и другие их аккаунты. Пароли надо **медленно и индивидуально хешировать** (Argon2, bcrypt), чтобы их было дорого взломать даже украденными. **То, что хеш медленный, — это смысл, а не недостаток.**

_Diagram: Быстрый хеш — дешёвые догадки, пароли вскрывают быстро. Медленный солёный хеш — каждая догадка дорогая._

#### Plaintext / Fast Hash (пароли текстом или в MD5)

- **Что это:** Пароли в базе как есть или под быстрым хешем.
- **Чем грозит:** Одна утечка — все пароли; переиспользованные пароли открывают и чужие аккаунты.
- **Как чинить:** Медленный хеш с солью: Argon2 или bcrypt.
- **Связи:** Правильная версия — **Password Hashing**.

### Token Leakage — утечка и доверие токенам

> Написать код от сигнализации на стикере, приклеенном к двери, — замок в порядке; ты просто раздал секрет.

**Token leakage** — утечка токенов и ключей. Они утекают через **URL (которые логируются), браузерный `localStorage` (который может прочитать скрипт) и секреты, закоммиченные в git.** И токен надо _проверять_, а не доверять ему лишь потому, что он появился, — подделанный или истёкший надо отклонить. Относись к любому креденшлу как к тому, что однажды утечёт: короткий срок жизни, ротация и никогда — там, где это логируется или скрейпится.

_Diagram: Три типичных пути утечки: URL в логах, localStorage и git. Защита: сервер проверяет каждый токен, срок жизни короткий, ключи ротируются._

#### Token Leakage (утечка токенов и ключей)

- **Что это:** Креденшл попадает туда, где его можно прочитать или найти.
- **Как выглядит:** `?token=` в URL, токен в `localStorage`, ключ в git-коммите.
- **Чем грозит:** Кто нашёл токен — тот вошёл.
- **Как чинить:** Проверять подпись и срок; короткая жизнь, ротация; **httpOnly Cookie** в браузере.
- **Связи:** Больнее всего для **JWT** и **API Key**.

### Trusting the Client — доверие клиенту

> Магазин, который позволяет покупателям самим писать ценники и никогда не сверяет их на кассе.

Анти-паттерн **trusting the client**. Всё, что шлёт клиент — скрытое поле формы, роль в куке, флаг `isAdmin=false`, — **можно подделать.** Браузер, приложение, запрос — всё под контролем пользователя. Каждая настоящая проверка должна происходить на **сервере**, который пользователь править не может. **Клиент — это предложение; сервер — это власть.**

_Diagram: Всё на стороне клиента пользователь может поменять. Решение принимает только сервер._

#### Trusting the Client (доверие клиенту)

- **Что это:** Решение о правах принимают по данным, которые прислал клиент.
- **Как выглядит:** Роль в куке, скрытое поле, флаг `isAdmin` в запросе.
- **Чем грозит:** Пользователь сам себе выдаёт права.
- **Как чинить:** Все проверки — на сервере; клиент только предлагает.
- **Связи:** Частая причина **Broken Access Control**.

> **Вопрос про провалы:** «Если пользователь подделает id, токен или запрос, остановит ли его всё равно мой сервер?»

> Почти любая утечка auth — это одно из трёх: пропущенная проверка, плохо хранимый секрет или поверили клиенту.

## 7. Сделать правильно

Хороший auth — это в основном про то, чтобы правильно использовать проверенные инструменты и неустанно проверять права, а не про изобретательность. Вот как выбирать.

### Auth Provider — используй библиотеку или провайдера

> Ты не производишь себе ремни безопасности — ты ставишь сертифицированные, спроектированные специалистами и испытанные в краш-тестах годами.

Почти для всех правильный ход — **не строить auth самому:** используй auth своего фреймворка или **auth provider** (Auth0, Clerk, Supabase, Cognito и подобные), который берёт на себя хеширование, сессии, MFA, OAuth и краевые случаи, о которых ты бы и не подумал. Твоя работа — корректно его подключить и владеть **авторизацией** — той частью, которую знаешь только ты.

_Diagram: Провайдер отвечает за личность: хеши, сессии, MFA, OAuth. Твоё приложение отвечает за правила доступа._

#### Auth Provider (auth-библиотека или сервис)

- **Проблема:** Своими руками auth почти всегда получается с дырами.
- **Идея:** Отдать authN проверенному инструменту, себе оставить authZ.
- **Пример:** Auth0, Clerk, Supabase, Cognito или встроенный auth фреймворка.
- **Цена:** Зависимость от поставщика; его надо правильно подключить.
- **Когда брать:** Почти всегда.
- **Связи:** Реализует **Don't Roll Your Own Auth**.

### Подбери механизм под приложение

> Ключ от дома, ключ от машины и карта-ключ от отеля — всё это «ключи»; выбираешь ты по двери, а не по моде.

**Сессии** для обычного веб-приложения (просто, мгновенно отзываемо); **короткоживущие JWT плюс refresh**, когда нужен безсостоятельный масштаб по сервисам; **OIDC** для соц-логина и SSO; **API-ключи** для «машина — машина». Добавляй **MFA** везде, где это важно, особенно на админских аккаунтах. Выбирай по форме своего приложения, а не по тому, что звучит продвинуто.

_Diagram: Задача слева — механизм справа. Выбор по форме приложения._

#### Прежде чем выкатывать auth

- **Пароли медленно хешируются** (Argon2/bcrypt) и никогда не хранятся текстом?
- **Доступна ли MFA** и обязательна ли для админов?
- **Каждый ли эндпоинт проверяет authZ** — роль _и_ владение объектом?
- **Токены в httpOnly-куках**, короткоживущие и никогда не в URL или git?
- **Настоящая проверка на сервере**, без доверия ролям от клиента?
- **Использую ли я проверенную библиотеку/провайдера** вместо самопальной схемы?

#### Признаки, что это сломано

- Смена **id в URL** показывает чужие данные.
- **Право живёт в куке или скрытом поле**, которое клиент может править.
- Пароли хранятся **текстом или в MD5**.
- **Нет способа отозвать** залогиненного пользователя, кроме ожидания истечения.
- **Нет MFA** на аккаунтах, способных нанести больше всего урона.
- Ты **написал свою** схему токенов или крипту.

#### Признаки, что ты сделал правильно

- AuthN и authZ **раздельны**, и authZ проверяется на **каждый запрос**.
- Каждый доступ к объекту проверяет, что **этот пользователь владеет этой вещью**.
- Креденшлы **хешированы, ограничены по области, короткоживущи и ротируемы.**
- **Сервер — это власть**; ничто не доверяет клиенту.
- Ты можешь **отозвать доступ** и увидеть **кто что сделал** в логе.
- Тяжёлая работа лежит в **проверенной библиотеке или провайдере**, а не в твоём коде.

### Как всё связано

> Путь гостя в отеле: паспорт на ресепшене, карта-ключ на руках, и каждая дверь сама решает, открыться ли.

Факторы входа (пароль, MFA, passkey, OIDC, API-ключ) ведут к **authN**. Её результат едет с каждым запросом в **сессии** или **JWT**, лучше всего в **httpOnly-куке**. На каждом запросе **authZ** решает по ролям или политике — и всегда проверяет **владение** объектом.

_Diagram: Слева направо: факторы → authN → сессия или JWT → authZ на каждый запрос → роли/политика + владение._

## Шпаргалка: все 22 понятия

### Два вопроса

- **Authentication (authN)** (аутентификация): Подтвердить, кто ты. Обычно один раз за вход.
- **Authorization (authZ)** (авторизация): Решить, что тебе можно. На каждый запрос.
- **Don't Roll Your Own Auth** (не делай свой auth): Бери проверенные стандарты и библиотеки, а не свою схему.

### Подтвердить, кто ты

- **Password Hashing** (хеширование паролей): Храни медленный солёный хеш (Argon2, bcrypt), а не пароль.
- **MFA** (многофакторка, 2FA): Второй фактор: что-то имеешь или чем являешься. Самый дешёвый выигрыш.
- **Passkeys** (WebAuthn): Пара ключей на устройстве, вход по подписи. Фишить нечего.

### Оставаться залогиненным

- **Stateless HTTP** (HTTP без состояния): Сервер не помнит запросы — нужен пропуск в каждом.
- **Session** (серверная сессия): В куке только id, правда — на сервере. Мгновенный отзыв. Дефолт.
- **JWT** (JSON Web Token): Подписанный токен с правами внутри. Без поиска, но трудно отозвать.
- **httpOnly Cookie** (кука без доступа из JS): Место для токена в браузере. XSS её не прочитает.

### Делегированный доступ

- **OAuth 2.0** (делегированная авторизация): Доступ к чужому аккаунту по токену с узким scope, без пароля.
- **OIDC** (OpenID Connect): OAuth2 + ID token: «и вот кто это». Соц-логин и SSO.
- **API Key** (API-ключ): Секрет для программ. Узкий scope, ротация, не в git и не в URL.

### Права

- **Least Privilege** (deny by default): По умолчанию «нет»; выдавай только нужный минимум.
- **RBAC** (Role-Based Access Control): Права по ролям: admin, editor, viewer.
- **ABAC** (Attribute-Based Access Control): Права по политике над атрибутами пользователя, ресурса, контекста.
- **Ownership Check** (проверка владения): Не только «какая роль», но и «чья запись».

### Как ломается

- **Broken Access Control / IDOR** (сломанный контроль доступа): Забыли проверить права: поменял id в URL — видишь чужое.
- **Plaintext / Fast Hash** (пароли текстом или в MD5): Одна утечка базы — все пароли наружу.
- **Token Leakage** (утечка токенов): Токены в URL, localStorage, git. Кто нашёл — тот вошёл.
- **Trusting the Client** (доверие клиенту): Клиент — предложение, сервер — власть.

### Сделать правильно

- **Auth Provider** (auth-библиотека или сервис): Отдай authN проверенному инструменту, authZ оставь себе.

> Auth — это не место, где умничают. Это место, где правильно используют стандарты, отказывают по умолчанию и никогда, ни за что не доверяют клиенту.

Конец экспресс-курса · 7 глав · 22 понятия

Дальше — глубина: OWASP Top 10 и её шпаргалки, спецификации OAuth 2.0 и OpenID Connect и гайды твоего auth-провайдера. Но носи с собой два вопроса всюду — кто ты и что тебе можно? На первый отвечай проверенным инструментом, а на второй — на своём сервере, каждый раз без исключений.
