fedorthinks

Экспресс-курс · No. 08

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

22 понятия · схема у каждого · шпаргалка в конце

§ 01

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

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

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

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

Credentialpassword · passkey · tokenVerifyAuthNIdentityuser = Aliceanswers one question: WHO is this?
AuthN превращает креденшл (пароль, passkey, токен) в личность — и отвечает только на вопрос «кто это».
ConceptAuthentication (authN)· аутентификация
Что это
Проверка, что пользователь — тот, за кого себя выдаёт.
Как работает

Пользователь предъявляет креденшл (пароль, passkey, токен), система его проверяет и устанавливает личность.

Пример
Логин по email и паролю, «это Алиса».
Подвох
Знать, кто человек, — ещё не значит знать, что ему можно.
Связи

Вход для Authorization; способы — Password Hashing, MFA, Passkeys, OIDC.

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

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

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

WhoAlice, editorActioneditResourcedoc #42Policy checkAuthZAllowDenyon every request
AuthZ берёт «кто», «что делает» и «над чем» — и на каждый запрос выносит решение: разрешить или отказать.
ConceptAuthorization (authZ)· авторизация
Что это
Решение, что этой личности можно делать с этим ресурсом.
Как работает

На каждый запрос: кто + действие + ресурс → правило → разрешить или отказать.

Пример

Алиса-редактор может править документ #42, но не может открыть админку.

Подвох

Проверить один раз и забыть. Проверять надо каждый запрос, на сервере.

Связи

Модели — Least Privilege, RBAC, ABAC, Ownership Check; провал — Broken Access Control.

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

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

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

front door: who are you?every inner door: may you?UserAuthNonce per loginGET /orders/7AuthZ checkPUT /docs/3AuthZ checkDELETE /users/9AuthZ check
AuthN — одна входная дверь. AuthZ — проверка у каждой внутренней двери, то есть на каждом запросе.

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

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

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

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

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

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

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

Two questionsAuthenticationAuthorizationNo DIY authProving identityPassword hashingMFAPasskeysStaying logged inSessionJWThttpOnly cookieDelegated accessOAuth 2.0OIDCAPI keyPermissionsLeast privilegeRBACABACOwnership checkFailure modesIDORToken leakageTrusting the client
Шесть групп понятий — по ним идут главы 1–6. Глава 7 — как выбрать и шпаргалка.

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

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

§ 02

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

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

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

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

Passwordhunter2+ saltrandom, per userSlow hashArgon2 · bcryptDB stores$argon2id$…Stolen DBonly hashescracking is slow, one user at a time
Пароль + случайная соль → медленный хеш → в базе только хеш. Украденная база не отдаёт пароли.
PracticePassword Hashing· хеширование паролей
Проблема
Пароли в базе текстом: одна утечка — и все пароли у атакующего.
Идея

Хранить не пароль, а медленный хеш с солью (salt) — своей для каждого пользователя.

Пример
При входе считаешь Argon2/bcrypt от введённого пароля и сравниваешь.
Цена
Вход чуть медленнее — и это нарочно.
Когда брать
Всегда, если хранишь пароли.
Связи
Анти-пример — Plaintext / Fast Hash; сверху добавь MFA.

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

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

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

Something you knowpasswordSomething you haveTOTP app · security keySomething you arefingerprint · faceAccess2+ factorsany 2 of 3SMS code: weak (interceptable)
Три вида факторов: знаешь, имеешь, являешься. Для входа нужны минимум два разных.
PracticeMFA· Multi-Factor Authentication, 2FA
Проблема
Один украденный пароль = полный вход в аккаунт.
Идея
Требовать второй фактор другого типа: «имеешь» или «являешься».
Пример
Пароль + шестизначный TOTP-код из приложения-аутентификатора.
Цена
Лишний шаг при входе; SMS-коды можно перехватить.
Когда брать
Везде, где это важно; на админских аккаунтах — обязательно.
Связи
Укрепляет Password Hashing; Passkeys — следующий шаг.

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

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

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

Your deviceholds private keyreal-site.comstores public keyfake-site.comwrong origin1. challenge2. signature✗ no signature herekey never leaves device
Сайт шлёт вызов, устройство отвечает подписью. Фишинговый сайт с другим адресом подписи не получит.
ConceptPasskeys· WebAuthn, вход без пароля
Проблема
Пароли фишат, переиспользуют и сливают.
Идея

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

Пример
Вход по отпечатку или Face ID вместо ввода пароля.
Цена
Нужна поддержка устройств и запасной путь восстановления.
Когда брать

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

Связи
Заменяет Password Hashing; сам по себе сильнее MFA по SMS.

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

§ 03

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

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

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

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

Request 1+ cookie / tokenit's still meRequest 2+ cookie / tokenit's still meRequest 3+ cookie / tokenit's still meServerforgets between calls
Сервер не помнит прошлых запросов, поэтому каждый запрос несёт с собой куку или токен.
ConceptStateless HTTP· HTTP без состояния
Что это
Сервер не помнит предыдущие запросы клиента.
Следствие
После входа нужен «пропуск», который едет с каждым запросом.
Пример
Браузер сам прикладывает куку к каждому запросу на этот сайт.
Связи
Два ответа — Session и JWT.

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

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

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

Browsercookie: sid=8f3a…Serverlooks up the sidSession store8f3a → AliceDelete sessionlogout / ban = instantrevoke
В куке только id. Сервер ищет его в хранилище сессий. Удалил сессию — пользователь вышел сразу.
PatternSession· серверная сессия
Проблема
HTTP не помнит, кто ты, между запросами.
Идея
Сервер хранит данные входа, клиент держит только случайный session id.
Пример
Кука sid=8f3a… → в хранилище 8f3a → Алиса.
Цена
Серверу нужно хранилище и поиск на каждый запрос.
Когда брать
Обычное веб-приложение. Это дефолт.
Связи

Альтернатива — JWT; id хранится в httpOnly Cookie; отзыв — мгновенный.

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

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

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

headeralgpayloadsub: alice · role · expsignaturesigned by issuer..Clientholds the JWTAPIverify signatureLeaked JWTvalid until expno lookup · statelessso: short expiry + refresh token
JWT = header.payload.signature. API проверяет подпись без базы. Утёкший токен живёт до exp — отсюда короткий срок и refresh-токен.
PatternJWT· JSON Web Token
Проблема
Много сервисов, и каждому лезть в общее хранилище сессий — дорого.
Идея
Положить личность и права в сам токен и подписать его.
Пример

Payload sub: alice, role: editor, exp: …; каждый сервис проверяет подпись сам.

Цена
Трудно отозвать: утёкший токен валиден до истечения.
Когда брать
API и микросервисы — короткоживущий JWT + refresh token.
Связи
Альтернатива Session; угроза — Token Leakage.

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

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

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

httpOnly cookieJS can't read itlocalStorageany script can read itInjected scriptXSSsteals token✗ blocked
Внедрённый скрипт (XSS) читает localStorage, но не может прочитать httpOnly-куку.
PracticehttpOnly Cookie· кука, закрытая от JavaScript
Проблема
Токен в localStorage крадёт любой внедрённый скрипт.
Идея
Хранить токен в куке с флагом httpOnly — JS её не видит.
Пример
Set-Cookie: sid=…; HttpOnly.
Цена
Почти никакой — это правильный дефолт для браузера.
Связи
Хранит Session id или JWT; защищает от Token Leakage.

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

§ 04

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

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

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

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

UserAuth serverGoogleYour appclientResource APICalendar1. logs in there + consentsyour app never sees the password2. access token · scope: read calendar3. call the API with the token
Пользователь входит у Google и даёт согласие. Приложение получает токен с узкой областью и ходит с ним в API. Пароль оно не видит.
ProtocolOAuth 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 — это стандарт.

"Log in with Google" · SSOOpenID Connect (OIDC)ID token: WHO the user isOAuth 2.0access token: WHAT the app may reachidentityaccess
OIDC — тонкий слой над OAuth 2.0: к access-токену («что можно») добавляется ID-токен («кто это»).
ProtocolOIDC· OpenID Connect
Проблема
OAuth2 даёт доступ, но не говорит, кто пользователь.
Идея
Добавить ID token с проверенными данными о личности.
Пример
Кнопка «Войти через Google»; вход в рабочие сервисы через SSO.
Цена
Зависишь от внешнего провайдера личности.
Когда брать
Соц-логин и single sign-on.
Связи
Построен на OAuth 2.0; это способ Authentication.

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

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

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

Service Aa program, not a personAPIchecks key + scopeheader: X-API-Key: sk_…✗ never in git✗ never in a URL↻ scope + rotate
Программа шлёт ключ в заголовке, API проверяет ключ и его область. Ключ не в git, не в URL, регулярно меняется.
ToolAPI Key· API-ключ
Проблема
Программе нужно входить в API без человека и без логина.
Идея
Длинный секрет, который опознаёт программу.
Пример
Бэкенд вызывает платёжный API с ключом в заголовке.
Цена
Ключ и есть доступ: нашёл — вошёл.
Когда брать
«Машина — машина». Не для входа людей.
Связи
Ограничивай по Least Privilege; главная угроза — Token Leakage.

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

§ 05

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

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

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

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

RequestExplicit grant?Allowthe minimum neededDenythe defaultyesno / unknown
Есть явное разрешение — пускаем, и только на нужный минимум. Нет или непонятно — отказ.
PrincipleLeast Privilege· deny by default, минимум привилегий
Проблема
Новый эндпоинт забыли закрыть — и он открыт всем.
Идея
По умолчанию «нет»; выдавать только нужный минимум.
Пример
Новый сотрудник получает доступ только к своему проекту.
Цена
Чаще приходится явно выдавать права.
Когда брать
Всегда — это основа любой модели прав.
Связи
Фундамент для RBAC, ABAC, Ownership Check.

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

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

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

usersrolespermissionsAliceadmineverythingBobeditorpublish · editChenviewerread
Пользователь → роль → набор прав. Права выдаются ролям, а не людям лично.
ModelRBAC· Role-Based Access Control
Проблема
Выдавать права каждому человеку по отдельности — хаос.
Идея
Права — ролям, роли — пользователям.
Пример
«Редакторы могут публиковать, зрители — только читать».
Цена
Грубо, когда правило зависит от контекста или конкретной записи.
Когда брать
Большинство приложений.
Связи
Тоньше — ABAC; всегда дополняй Ownership Check.

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

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

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

User attributesrole = managerResource attributesexpense: 8kContext14:00 · company laptopPolicyamount < 10k AND …Allow / Denyevaluated every time
Атрибуты пользователя, ресурса и контекста идут в политику, которая выносит решение заново на каждый запрос.
ModelABAC· Attribute-Based Access Control
Проблема
Права зависят не только от роли, но и от суммы, времени, устройства.
Идея
Правило-политика над атрибутами пользователя, ресурса и контекста.
Пример
Менеджер одобряет расход до 10k, в рабочие часы, с рабочего ноутбука.
Цена
Гораздо сложнее писать, читать и тестировать.
Когда брать
Когда к ролям прилипает больше пары кастомных if.
Связи
Развитие RBAC; стоит на Least Privilege.

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

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

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

PUT /docs/42by BobRole checkeditor? ✓Owner checkdoc 42 owner = Bob?Allowonly if both passthe most-missed check
Две проверки: роль («редактор?») и владение («этот документ — его?»). Пускаем, только если прошли обе.
PracticeOwnership Check· проверка владения, object-level authZ
Проблема
Роль разрешает «править документы» — и человек правит чужие.
Идея
Кроме роли проверять, что этот объект принадлежит этому пользователю.
Пример

Перед PUT /docs/42 проверить: владелец документа 42 — текущий пользователь.

Цена
Лишняя проверка на каждый доступ к объекту.
Когда брать
Всегда, когда у данных есть владелец.
Связи
Дополняет RBAC/ABAC; без неё — IDOR.

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

§ 06

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

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

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

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

Boblogged inGET /orders/123Bob's order ✓GET /orders/124Ann's order!ServerauthN ✓ · owner check ✗fix: WHERE id = 124 AND owner = current_user
Боб вошёл честно, но сервер не проверяет владение — поменял id в URL и видишь чужой заказ.
Anti-patternBroken 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), чтобы их было дорого взломать даже украденными. То, что хеш медленный, — это смысл, а не недостаток.

MD5 / SHA-1fast hashCracked fastevery leaked user exposedArgon2 / bcryptslow + saltedToo costlyeven when stolenattacker guesses: cheapattacker guesses: expensive
Быстрый хеш — дешёвые догадки, пароли вскрывают быстро. Медленный солёный хеш — каждая догадка дорогая.
Anti-patternPlaintext / Fast Hash· пароли текстом или в MD5
Что это
Пароли в базе как есть или под быстрым хешем.
Чем грозит

Одна утечка — все пароли; переиспользованные пароли открывают и чужие аккаунты.

Как чинить
Медленный хеш с солью: Argon2 или bcrypt.
Связи
Правильная версия — Password Hashing.

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

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

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

Token / API key?token= in URLends up in logslocalStorageread by XSSgit commitpublic foreverServer verifiessignature · expiry · scopeshort-lived · rotatable
Три типичных пути утечки: URL в логах, localStorage и git. Защита: сервер проверяет каждый токен, срок жизни короткий, ключи ротируются.
Anti-patternToken Leakage· утечка токенов и ключей
Что это
Креденшл попадает туда, где его можно прочитать или найти.
Как выглядит
?token= в URL, токен в localStorage, ключ в git-коммите.
Чем грозит
Кто нашёл токен — тот вошёл.
Как чинить

Проверять подпись и срок; короткая жизнь, ротация; httpOnly Cookie в браузере.

Связи
Больнее всего для JWT и API Key.

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

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

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

CLIENT · user-controlledhidden form fieldcookie: role=adminisAdmin=falseServerre-checks everything itselfsuggestionthe authority
Всё на стороне клиента пользователь может поменять. Решение принимает только сервер.
Anti-patternTrusting the Client· доверие клиенту
Что это
Решение о правах принимают по данным, которые прислал клиент.
Как выглядит
Роль в куке, скрытое поле, флаг isAdmin в запросе.
Чем грозит
Пользователь сам себе выдаёт права.
Как чинить
Все проверки — на сервере; клиент только предлагает.
Связи
Частая причина Broken Access Control.

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

§ 07

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

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

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

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

Auth library / providerPassword hashingSessions · tokensMFA · passkeysOAuth · OIDCYour appowns AuthZ: the rulesidentity
Провайдер отвечает за личность: хеши, сессии, MFA, OAuth. Твоё приложение отвечает за правила доступа.
ToolAuth Provider· auth-библиотека или сервис
Проблема
Своими руками auth почти всегда получается с дырами.
Идея
Отдать authN проверенному инструменту, себе оставить authZ.
Пример
Auth0, Clerk, Supabase, Cognito или встроенный auth фреймворка.
Цена
Зависимость от поставщика; его надо правильно подключить.
Когда брать
Почти всегда.
Связи
Реализует Don't Roll Your Own Auth.

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

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

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

Normal web appSessionsMany services / APIsShort JWT + refreshSocial login / SSOOIDCMachine to machineAPI keyAdmin / high risk+ MFA
Задача слева — механизм справа. Выбор по форме приложения.
Прежде чем выкатывать auth
  • Пароли медленно хешируются (Argon2/bcrypt) и никогда не хранятся текстом?
  • Доступна ли MFA и обязательна ли для админов?
  • Каждый ли эндпоинт проверяет authZ — роль и владение объектом?
  • Токены в httpOnly-куках, короткоживущие и никогда не в URL или git?
  • Настоящая проверка на сервере, без доверия ролям от клиента?
  • Использую ли я проверенную библиотеку/провайдера вместо самопальной схемы?
Признаки, что это сломано
  • Смена id в URL показывает чужие данные.
  • Право живёт в куке или скрытом поле, которое клиент может править.
  • Пароли хранятся текстом или в MD5.
  • Нет способа отозвать залогиненного пользователя, кроме ожидания истечения.
  • Нет MFA на аккаунтах, способных нанести больше всего урона.
  • Ты написал свою схему токенов или крипту.
Признаки, что ты сделал правильно
  • AuthN и authZ раздельны, и authZ проверяется на каждый запрос.
  • Каждый доступ к объекту проверяет, что этот пользователь владеет этой вещью.
  • Креденшлы хешированы, ограничены по области, короткоживущи и ротируемы.
  • Сервер — это власть; ничто не доверяет клиенту.
  • Ты можешь отозвать доступ и увидеть кто что сделал в логе.
  • Тяжёлая работа лежит в проверенной библиотеке или провайдере, а не в твоём коде.

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

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

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

Password + hashMFAPasskeyOAuth2 / OIDCAPI keyAuthNwho?SessionJWThttpOnly cookiecarries itAuthZmay?every requestRBAC / ABACOwnership
Слева направо: факторы → authN → сессия или JWT → authZ на каждый запрос → роли/политика + владение.

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

Два вопроса

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

Подтвердить, кто ты. Обычно один раз за вход.

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

Решить, что тебе можно. На каждый запрос.

Don't Roll Your Own Authне делай свой auth

Бери проверенные стандарты и библиотеки, а не свою схему.

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

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

Храни медленный солёный хеш (Argon2, bcrypt), а не пароль.

MFAмногофакторка, 2FA

Второй фактор: что-то имеешь или чем являешься. Самый дешёвый выигрыш.

PasskeysWebAuthn

Пара ключей на устройстве, вход по подписи. Фишить нечего.

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

Stateless HTTPHTTP без состояния

Сервер не помнит запросы — нужен пропуск в каждом.

Sessionсерверная сессия

В куке только id, правда — на сервере. Мгновенный отзыв. Дефолт.

JWTJSON Web Token

Подписанный токен с правами внутри. Без поиска, но трудно отозвать.

httpOnly Cookieкука без доступа из JS

Место для токена в браузере. XSS её не прочитает.

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

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

Доступ к чужому аккаунту по токену с узким scope, без пароля.

OIDCOpenID Connect

OAuth2 + ID token: «и вот кто это». Соц-логин и SSO.

API KeyAPI-ключ

Секрет для программ. Узкий scope, ротация, не в git и не в URL.

Права

Least Privilegedeny by default

По умолчанию «нет»; выдавай только нужный минимум.

RBACRole-Based Access Control

Права по ролям: admin, editor, viewer.

ABACAttribute-Based Access Control

Права по политике над атрибутами пользователя, ресурса, контекста.

Ownership Checkпроверка владения

Не только «какая роль», но и «чья запись».

Как ломается

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

Забыли проверить права: поменял id в URL — видишь чужое.

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

Одна утечка базы — все пароли наружу.

Token Leakageутечка токенов

Токены в URL, localStorage, git. Кто нашёл — тот вошёл.

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

Клиент — предложение, сервер — власть.

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

Auth Providerauth-библиотека или сервис

Отдай authN проверенному инструменту, authZ оставь себе.

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

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

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