Модуль: Backend · Уровень: Middle+/Senior
TL;DR#
- Аутентификация (AuthN) — «кто ты?» (проверка личности). Авторизация (AuthZ) — «что тебе можно?» (проверка прав). Это два независимых шага: сначала AuthN, потом AuthZ.
- Сессии (stateful): server-side store + opaque cookie. Легко отозвать, но требует общего стора и липкости/репликации. JWT (stateless): self-contained подпись, масштабируется горизонтально, но невозможно мгновенно отозвать без денилиста. Не путайте «stateless токен» с «нет состояния вообще».
- Cookie: всегда
HttpOnly,Secure,SameSite. Защита от XSS (HttpOnly) и CSRF (SameSite/anti-CSRF token) — это разные угрозы, нужны оба слоя. - Пароли: только
argon2id(предпочтительно) /bcrypt/scryptс уникальной солью. Никогда plaintext/MD5/SHA-256 без KDF. Сравнение — constant-time. - RBAC (роли) для простых иерархий; ABAC (атрибуты + политики) для контекстных решений; ReBAC (Zanzibar/SpiceDB) для графов отношений («владелец документа», sharing).
- API keys — для machine-to-machine: хранить только хэш, скоупить, ротировать. mTLS — взаимная аутентификация по сертификатам, типично в service mesh.
- Секреты: не в git, не хардкодить. Env vars — минимум; Vault/Secrets Manager + ротация + encryption at rest — для прода. Принцип наименьших привилегий + defense in depth везде.
Теория#
1. Аутентификация (AuthN) vs Авторизация (AuthZ)#
Это самая базовая и самая часто путаемая пара понятий.
| Аспект | Аутентификация (AuthN) | Авторизация (AuthZ) |
|---|---|---|
| Вопрос | «Кто ты?» | «Что тебе разрешено?» |
| Цель | Подтвердить личность | Принять решение о доступе |
| Когда | Первый шаг | После успешной AuthN |
| Артефакты | Пароль, OTP, сертификат, биометрия | Роли, разрешения, политики, ACL |
| Ошибка | 401 Unauthorized (несмотря на название — это про AuthN!) | 403 Forbidden |
| Меняется ли | Редко (личность стабильна) | Часто (права меняются динамически) |
Тонкость HTTP-статусов:
401 Unauthorizedисторически назван неверно — это «не аутентифицирован» (нет/невалидны креды).403 Forbidden— «аутентифицирован, но прав не хватает». На senior это любят спрашивать.
// Псевдо-pipeline: сначала AuthN, потом AuthZ
func AuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// AuthN: кто ты?
user, err := authenticate(r) // парсит токен/сессию -> личность
if err != nil {
w.WriteHeader(http.StatusUnauthorized) // 401: не доказал, кто ты
return
}
ctx := context.WithValue(r.Context(), userKey{}, user)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func RequirePermission(perm string, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user := r.Context().Value(userKey{}).(*User)
// AuthZ: что тебе можно?
if !user.Can(perm) {
w.WriteHeader(http.StatusForbidden) // 403: ты известен, но нельзя
return
}
next.ServeHTTP(w, r)
})
}Идентификация vs аутентификация: идентификация — это заявление «я Алиса» (username). Аутентификация — доказательство этого заявления (пароль/токен). Email != аутентификация.
2. Сессии (stateful) vs токены (stateless JWT)#
Сессии (stateful)#
Сервер генерирует случайный непрозрачный (opaque) session ID, кладёт его в Set-Cookie, а связанные данные хранит на сервере (Redis/Postgres/память).
// Создание сессии
sid := randomToken(32) // криптослучайный, 256 бит
store.Set(ctx, "sess:"+sid, SessionData{UserID: u.ID, ...}, 24*time.Hour)
http.SetCookie(w, &http.Cookie{
Name: "sid",
Value: sid,
HttpOnly: true,
Secure: true,
SameSite: http.SameSiteLaxMode,
Path: "/",
MaxAge: 86400,
})Токены (stateless, JWT)#
JWT = base64url(header).base64url(payload).base64url(signature). Сервер подписывает (HMAC HS256 или асимметрично RS256/ES256/EdDSA) и при каждом запросе проверяет подпись, не обращаясь к хранилищу. Данные (claims) лежат в payload — он подписан, но не зашифрован (base64, читается любым).
import "github.com/golang-jwt/jwt/v5"
claims := jwt.MapClaims{
"sub": u.ID,
"exp": time.Now().Add(15 * time.Minute).Unix(), // короткий TTL!
"iat": time.Now().Unix(),
"scope": "read:orders write:orders",
}
tok := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
signed, _ := tok.SignedString(privateKey)
// Проверка — ОБЯЗАТЕЛЬНО фиксируем допустимые алгоритмы
parsed, err := jwt.Parse(signed, func(t *jwt.Token) (any, error) {
if _, ok := t.Method.(*jwt.SigningMethodECDSA); !ok {
return nil, fmt.Errorf("unexpected alg: %v", t.Header["alg"]) // защита от alg-confusion
}
return publicKey, nil
}, jwt.WithValidMethods([]string{"ES256"}))Таблица сравнения#
| Критерий | Сессии (stateful) | JWT (stateless) |
|---|---|---|
| Хранение состояния | Сервер (Redis/БД) | Клиент (токен сам несёт claims) |
| Масштабирование | Нужен общий стор или липкие сессии | Любая нода проверяет по ключу — горизонтально просто |
| Revocation (отзыв) | Мгновенно: удалил из стора | Нельзя до exp без денилиста/короткого TTL |
| Размер на запрос | Маленький cookie (ID) | Большой (весь payload в каждом запросе) |
| Утечка данных | На сервере, клиент видит только ID | Payload читается любым (не класть PII/секреты) |
| Изменение прав | Сразу при следующем запросе | Только после ре-выпуска токена |
| Транспорт | Обычно cookie | Cookie или Authorization: Bearer |
| Типичный риск | CSRF (если cookie) | XSS (если хранить в localStorage) |
Revocation JWT — основная боль#
JWT нельзя «отозвать» по дизайну. Решения:
- Короткий TTL access-токена (5–15 мин) + долгоживущий refresh-токен, который хранится stateful (в БД) и отзывается. Это де-факто стандарт.
- Денилист (blocklist) отозванных
jtiв Redis — но это возвращает stateful-зависимость, теряется главное преимущество. - Token versioning: в claims кладём
token_version, сравниваем с версией в БД (инкремент = отзыв всех токенов юзера). Но это снова чтение БД на каждый запрос.
Вывод: «полностью stateless» auth — миф для систем, где нужен мгновенный logout/ban. Refresh-токен всё равно stateful.
Где хранить токен на клиенте (CSRF vs XSS)#
| Место хранения | XSS-риск | CSRF-риск | Комментарий |
|---|---|---|---|
localStorage | Высокий (JS читает токен) | Нет | Любой XSS = кража токена |
HttpOnly cookie | Низкий (JS не читает) | Есть | Нужен SameSite + anti-CSRF token |
| In-memory (JS-переменная) | Средний | Нет | Теряется при reload, нужен refresh-flow |
Лучший практичный вариант для SPA: access-токен в памяти + refresh-токен в HttpOnly Secure SameSite cookie.
3. Cookie-атрибуты#
Set-Cookie: sid=abc; HttpOnly; Secure; SameSite=Lax; Path=/; Domain=example.com; Max-Age=86400| Атрибут | Назначение |
|---|---|
HttpOnly | Cookie недоступна из JS (document.cookie) — митигирует кражу при XSS |
Secure | Отправляется только по HTTPS — защита от перехвата по сети |
SameSite=Strict | Не шлётся при любых кросс-сайтовых запросах (даже по клику из письма) — максимум защиты от CSRF, но ломает «переход извне» |
SameSite=Lax | Шлётся при top-level навигации GET, но не при POST/iframe/fetch с другого сайта — разумный дефолт (и дефолт браузеров) |
SameSite=None | Шлётся всегда; требует Secure. Для кросс-доменных сценариев (виджеты, сторонние API) |
Domain | Какие хосты получают cookie. Без Domain — только точный хост; с Domain=example.com — и поддомены |
Path | Ограничивает cookie путём URL |
__Host- / __Secure- префиксы | Браузер форсит требования: __Host- требует Secure, Path=/, без Domain — сильная защита от подмены |
SameSiteснижает CSRF, но не заменяет anti-CSRF токены полностью (старые браузеры,SameSite=None, GET-мутации). Defense in depth:SameSite=Lax+ double-submit/synchronizer token для мутаций.
4. Хранение паролей#
Никогда не храните plaintext. Никогда не используйте «голые» хэши (MD5, SHA-1, SHA-256) — они быстрые, что и нужно атакующему для брутфорса/радужных таблиц. Нужен медленный KDF с солью.
| Алгоритм | Тип | Параметры | Рекомендация |
|---|---|---|---|
argon2id | Memory-hard | memory, iterations, parallelism | Предпочтительно (победитель PHC, стоек к GPU/ASIC) |
scrypt | Memory-hard | N, r, p | Хорошо, если нет argon2 |
bcrypt | CPU-hard | cost (work factor) | Ок, но лимит пароля 72 байта (молча обрезает!) |
| PBKDF2 | CPU-hard | iterations | Допустим (FIPS), но слабее против GPU |
import "golang.org/x/crypto/argon2"
// Регистрация
salt := make([]byte, 16)
rand.Read(salt) // crypto/rand!
hash := argon2.IDKey([]byte(password), salt,
3, // time (iterations)
64*1024, // memory KiB (64 MB)
4, // parallelism
32) // keyLen
// Хранить: alg, params, salt, hash (формат PHC-строки $argon2id$v=19$m=...,t=...,p=...$salt$hash)
// Проверка — constant-time сравнение!
import "crypto/subtle"
candidate := argon2.IDKey([]byte(input), salt, 3, 64*1024, 4, 32)
ok := subtle.ConstantTimeCompare(candidate, hash) == 1Ключевые моменты:
- Соль — уникальная на каждый пароль, делает радужные таблицы бесполезными. Хранится рядом с хэшем (не секрет).
- Pepper (опционально) — глобальный секрет, добавляемый к паролю, хранится отдельно (в Vault/HSM, не в БД). Защищает при утечке только БД.
- Constant-time сравнение (
subtle.ConstantTimeCompare) — обычное==для байтов сравнивает с ранним выходом и течёт время → timing attack. Для самого хэша argon2 сравнивает фиксированную длину, но для токенов/HMAC это критично. - Параметры со временем растут — храните их вместе с хэшем, чтобы апгрейдить (rehash при логине, если параметры устарели).
bcrypt 72-байтный лимит: всё после 72 байта игнорируется. Если предхэшировать паролем длиннее (например base64(SHA-256(pwd))) — следите за null-байтами, bcrypt обрезает по
\0. Argon2 этого лишён.
5. Модели авторизации: RBAC vs ABAC vs ReBAC#
| Модель | Идея | Решение основано на | Когда применять | Минусы |
|---|---|---|---|---|
| RBAC | Пользователь → роли → разрешения | Роль субъекта | Простые иерархии (admin/editor/viewer), enterprise | «Role explosion», плохо выражает контекст («только свои заказы») |
| ABAC | Политики над атрибутами субъекта/ресурса/среды | Атрибуты + правила | Контекстные/динамические решения (время, гео, отдел, статус документа) | Сложно отлаживать/аудировать, «почему доступ?» |
| ReBAC | Граф отношений между субъектами и ресурсами | Связь («owner of», «member of team») | Sharing, иерархии ресурсов, Google-Docs-like | Нужна спец. инфраструктура (Zanzibar/SpiceDB), сложность |
RBAC#
type Role struct {
Name string
Permissions []string // "orders:read", "orders:write"
}
func (u *User) Can(perm string) bool {
for _, r := range u.Roles {
if slices.Contains(r.Permissions, perm) {
return true
}
}
return false
}ABAC — политика как функция атрибутов#
// "Менеджер может читать заказы своего региона в рабочее время"
func canReadOrder(subj Subject, res Order, env Env) bool {
return subj.Role == "manager" &&
subj.Region == res.Region &&
env.Hour >= 9 && env.Hour < 18
}На практике политики выносят в движок: OPA/Rego, Cedar (AWS), Casbin.
ReBAC — Zanzibar / SpiceDB / OpenFGA#
Авторизация как проверка достижимости в графе отношений. Запись (tuple): document:readme#viewer@user:alice. Проверка: «может ли alice читать readme?» обходит граф (viewer ⊇ editor ⊇ owner, наследование через папки/группы).
// SpiceDB-схема (упрощённо)
definition document {
relation owner: user
relation viewer: user | group#member
permission read = viewer + owner
permission write = owner
}Применяют, когда права определяются отношениями объектов, а не статичными ролями (общие папки, вложенные команды). Решает проблему консистентности и «горячих» проверок на масштабе (как Google для Docs/Drive/YouTube).
На senior полезно: эти модели не взаимоисключающие. Часто RBAC для грубых ролей + ABAC/ReBAC для тонких решений. Главный архитектурный выбор — выносить ли AuthZ в централизованный сервис/sidecar (PDP — Policy Decision Point) или оставить в приложении (PEP — Policy Enforcement Point).
6. API keys (machine-to-machine)#
API key — длинный непрозрачный секрет для аутентификации сервиса/интеграции (не человека, у которого есть пароль+MFA).
Правила:
- Генерация: криптослучайный, с префиксом для опознания (
sk_live_...) — помогает сканерам секретов и саппорту. - Хранение: только хэш (как пароль, но т.к. ключ высокоэнтропийный — достаточно быстрого
SHA-256, KDF не обязателен). Показываем сырой ключ один раз при создании. - Скоупинг: ключ привязан к набору разрешений/ресурсов (наименьшие привилегии). Read-only ключ != admin ключ.
- Ротация: поддержка нескольких активных ключей одновременно, чтобы менять без даунтайма (issue new → migrate → revoke old).
- Метаданные: last_used, created_at, expiry — для аудита и протухания.
func newAPIKey() (display, hash string) {
raw := randomToken(32)
display = "sk_live_" + raw // отдаём пользователю ОДИН раз
sum := sha256.Sum256([]byte(raw))
hash = hex.EncodeToString(sum[:]) // в БД храним только это
return
}
// Проверка входящего ключа — найти по хэшу, constant-time на хэшеAPI key слабее OAuth/mTLS (один long-lived секрет, легко утекает в логи/git). Для M2M в проде предпочтительнее OAuth2 client credentials (короткие токены) или mTLS.
7. mTLS (взаимный TLS)#
Обычный TLS: клиент проверяет сертификат сервера. mTLS: ещё и сервер проверяет сертификат клиента. Обе стороны аутентифицируют друг друга криптографически.
// Сервер требует клиентский сертификат
caPool := x509.NewCertPool()
caPool.AppendCertsFromPEM(caPEM)
srv := &http.Server{
TLSConfig: &tls.Config{
ClientCAs: caPool,
ClientAuth: tls.RequireAndVerifyClientCert, // обязателен валидный клиентский cert
MinVersion: tls.VersionTLS13,
},
}
// Личность клиента — из r.TLS.PeerCertificates[0].Subject (CN / SAN)Когда применять:
- Service mesh (Istio/Linkerd): автоматический mTLS между подами, sidecar-прокси выпускает/ротирует короткоживущие сертификаты (SPIFFE/SPIRE) — zero-trust внутри кластера.
- B2B-интеграции с высокими требованиями (финтех, банки).
- Внутренние M2M, где не хотим раздавать долгоживущие секреты.
Плюсы: нет «секрета-пароля» в запросе, личность встроена в транспорт, сложно подделать. Минусы: управление сертификатами (выпуск, ротация, отзыв через CRL/OCSP), сложнее отлаживать, нагрузка на инфраструктуру PKI.
8. Где хранить секреты#
| Способ | Плюсы | Минусы |
|---|---|---|
| Хардкод в коде | — | Никогда. Утечёт в git/историю навсегда |
.env файл | Просто локально | Легко закоммитить, нет ротации/аудита |
| Env vars (в рантайме) | Просто, 12-factor | Видны в /proc, дампах, child-процессах, нет ротации |
| Secrets Manager (Vault, AWS/GCP) | Ротация, аудит, encryption at rest, dynamic secrets, ACL | Сложнее, зависимость, латентность |
| KMS/HSM | Ключи не покидают модуль | Дорого, для крипто-операций |
Best practices:
- Не коммитить:
.gitignoreдля.env, секрет-сканеры в CI (gitleaks, trufflehog), pre-commit hooks. Если секрет попал в git — он скомпрометирован, ротировать, а не просто удалять коммит (история остаётся). - Encryption at rest: секреты в хранилище шифруются (Vault использует master key/seal, облака — KMS).
- Ротация: автоматическая, регулярная. Vault dynamic secrets выдают короткоживущие креды БД по запросу (выдал на 1 час — отозвал).
- Доступ по наименьшим привилегиям: сервис читает только свои секреты (Vault policies, IAM роли). Аудит-лог каждого доступа.
- Не логировать: маскировать секреты в логах/трейсах/ошибках.
- Workload identity: вместо статичных кредов — IAM роль пода/инстанса (AWS IRSA, GCP Workload Identity), Vault auth по Kubernetes SA. Секрет «выдаётся» по доказанной личности, ничего не хардкодится.
// Антипаттерн
const dbPassword = "p@ssw0rd" // НИКОГДА
// Лучше: из секрет-менеджера в рантайме, с возможностью обновления
secret, err := vaultClient.KVv2("secret").Get(ctx, "myapp/db")
pwd := secret.Data["password"].(string)9. Сквозные принципы#
- Принцип наименьших привилегий (PoLP): каждый субъект (юзер, сервис, токен, IAM-роль) имеет минимум прав для задачи. Дефолт — deny. Скоупы у токенов/ключей.
- Defense in depth: несколько независимых слоёв. Пример для cookie-auth: TLS +
HttpOnly+Secure+SameSite+ anti-CSRF token + rate limiting + короткий TTL. Падение одного слоя не компрометирует систему. - Fail closed: при ошибке/неопределённости — запрещать, а не разрешать.
- Zero trust: не доверять сети по умолчанию, аутентифицировать каждый запрос (даже внутренний — отсюда mTLS в mesh).
Подводные камни / gotchas#
alg: noneи alg-confusion в JWT: если не фиксировать допустимые алгоритмы при верификации, атакующий ставитalg: none(без подписи) или подменяетRS256→HS256, используя публичный ключ как HMAC-секрет. Всегдаjwt.WithValidMethods([...]).- JWT payload не зашифрован: base64, не base64-«шифр». Не класть пароли, PII, секреты — только идентификаторы.
- «Stateless logout»: пользователь нажал «выйти», но JWT валиден до
exp. Без денилиста/короткого TTL токен продолжает работать — частая дыра. ==для сравнения токенов/HMAC: timing attack. Используйтеsubtle.ConstantTimeCompare.- bcrypt 72 байта: длинные пароли молча обрезаются; пароль из менеджера паролей может «совпасть» с другим. Использовать argon2id или предхэшировать аккуратно.
math/randдля токенов/солей: предсказуемо. Толькоcrypto/rand.SameSite=NoneбезSecure: браузер отбросит cookie. ИNoneотключает CSRF-защиту самой cookie.- Хранение JWT в
localStorage: один XSS = кража всех токенов. Лучше HttpOnly cookie или память. - Логирование токенов/ключей/паролей: утечка через логи/APM/Sentry — частый инцидент. Маскировать.
- Отсутствие ротации refresh-токенов: без rotation украденный refresh живёт долго. Применять refresh token rotation + reuse detection (повторное использование старого = компрометация, отозвать всю цепочку).
403vs404для приватных ресурсов: иногда отдают404вместо403, чтобы не раскрывать существование ресурса (enumeration). Зависит от модели угроз.- Доверие клиентским claims без проверки: роль/права в JWT валидны только если подпись проверена и issuer/audience совпадают (
iss,aud,exp,nbf). - Секреты в env при
docker inspect/crash dump: env-переменные видны многим; для high-security — секрет-менеджер с in-memory доставкой.
Вопросы на собеседовании#
В: В чём разница между аутентификацией и авторизацией, и какие HTTP-статусы им соответствуют?
О: AuthN — проверка личности («кто ты»), AuthZ — проверка прав («что можно»). Сначала AuthN, потом AuthZ. 401 Unauthorized — про AuthN (нет/невалидны креды), 403 Forbidden — про AuthZ (аутентифицирован, но прав нет). Название 401 исторически вводит в заблуждение.
В: Когда выбрать сессии, а когда JWT? О: Сессии — когда нужен мгновенный отзыв, простая модель, и есть общий стор (Redis); особенно для классических web-приложений с cookie. JWT — когда нужно горизонтальное масштабирование без общего стора, межсервисная передача identity, мобильные/SPA клиенты. Но «чистый stateless» ломается на logout/ban — почти всегда добавляют stateful refresh-токены. Гибрид: короткий JWT access + stateful refresh.
В: Как отозвать JWT до истечения срока?
О: По дизайну нельзя без состояния. Варианты: короткий TTL + отзываемый refresh-токен (стандарт); денилист jti в Redis (возвращает stateful); token_version в claims со сверкой в БД. Любой реальный отзыв требует серверного состояния — это компромисс с самой идеей stateless.
В: Чем отличаются угрозы XSS и CSRF, и как защититься?
О: XSS — выполнение чужого JS на странице (крадёт токены, действует от имени юзера). Защита: CSP, экранирование, HttpOnly cookie (JS не прочитает). CSRF — браузер автоматически шлёт cookie на запрос, инициированный чужим сайтом. Защита: SameSite=Lax/Strict, anti-CSRF токены (synchronizer/double-submit), проверка Origin/Referer. Это разные угрозы — нужны оба слоя (defense in depth).
В: Как правильно хранить пароли и что не так с SHA-256? О: Только медленный KDF с уникальной солью: argon2id (предпочтительно), bcrypt, scrypt. SHA-256/MD5 быстрые и без соли → уязвимы к брутфорсу на GPU и радужным таблицам. Соль уникальна на пароль, хранится с хэшем. Сравнение constant-time. Параметры KDF растут со временем (rehash при логине). Опционально pepper в отдельном хранилище.
В: RBAC vs ABAC vs ReBAC — когда что? О: RBAC — статичные роли/разрешения, простые иерархии, но «role explosion» и слабо выражает контекст. ABAC — политики над атрибутами (субъект/ресурс/среда), хорош для динамических/контекстных решений (время, гео, статус), сложнее аудит. ReBAC (Zanzibar/SpiceDB/OpenFGA) — авторизация как граф отношений, идеален для sharing и иерархий ресурсов (Google Docs). На практике комбинируют; ключевое решение — централизованный PDP или логика в приложении.
В: Что такое mTLS и где он уместен? О: Взаимный TLS: не только клиент проверяет сервер, но и сервер проверяет клиентский сертификат — обе стороны криптографически аутентифицированы. Уместен в service mesh (Istio/Linkerd с SPIFFE/SPIRE, авто-ротация коротких сертов, zero-trust), B2B-интеграциях, внутреннем M2M. Личность встроена в транспорт, нет «секрета-пароля» в запросе; цена — управление PKI (выпуск/ротация/отзыв).
В: Где хранить секреты в проде и почему не в env-переменных?
О: Лучше секрет-менеджер (Vault, AWS/GCP Secrets Manager): ротация, аудит, encryption at rest, dynamic secrets, ACL по наименьшим привилегиям. Env-переменные просты, но видны в /proc, дампах, child-процессах, без ротации/аудита. Никогда не коммитить (сканеры в CI, ротация при утечке). Идеал — workload identity (IAM-роль пода) вместо статичных кредов.
В: Как безопасно реализовать API keys для M2M?
О: Криптослучайный ключ с префиксом (sk_live_), хранить только хэш (SHA-256 достаточно из-за высокой энтропии), показывать сырой ключ один раз. Скоупить под минимальные права, поддержать несколько активных ключей для бесшовной ротации, метаданные (last_used, expiry). Для прода предпочтительнее OAuth2 client credentials или mTLS — API key это один долгоживущий секрет, легко утекает.
В: Какие атаки на JWT-верификацию вы знаете?
О: alg: none (подпись отключена), alg-confusion RS256→HS256 (публичный ключ используется как HMAC-секрет). Защита: всегда фиксировать допустимые алгоритмы (WithValidMethods), не доверять alg из заголовка, проверять iss/aud/exp/nbf. Также: незашифрованный payload (не класть секреты), отсутствие отзыва.
На что копают на senior+#
- Архитектура AuthZ на масштабе: где принимается решение — PEP в каждом сервисе vs централизованный PDP (OPA-sidecar, авторизационный сервис), как избежать «горячих» проверок и обеспечить консистентность (проблема, которую решает Zanzibar через зукиперы/тайм-стемпы).
- Полный refresh-flow: rotation, reuse detection, привязка refresh к устройству/family, отзыв всей цепочки при компрометации, sliding vs absolute expiration.
- Identity federation: OIDC поверх OAuth2, как валидировать токены IdP (JWKS, ротация ключей, кэш), SSO, SAML vs OIDC, audience/issuer изоляция между сервисами.
- Zero trust / SPIFFE: workload identity, SVID, авто-ротация сертов в mesh, отказ от долгоживущих секретов вовсе.
- Криптогигиена: выбор HS vs RS vs ES vs EdDSA, управление и ротация подписывающих ключей, key rolling без даунтайма (kid в заголовке), HSM/KMS для приватных ключей.
- Threat modeling конкретного флоу: умение разложить логин/refresh на угрозы (STRIDE), назвать слои defense in depth и failure modes (fail closed).
- Секреты как процесс, а не настройка: динамические креды, lease/TTL, аудит доступа, реакция на инцидент (ротация всего при утечке), отделение PoLP по сервисам.
- Производительность и кэш авторизации: как кэшировать решения без ослабления отзыва, инвалидция при изменении прав, decision logs для аудита и отладки «почему доступ».