Skip to content

Аутентификация и авторизация ​

Материал проходит ревью.

НовичокФундамент ~52 мин

Границы темы ​

Scope: путь обычного backend-запроса: credential подтверждает контроль identity, система создаёт principal/session и отдельно решает, разрешено ли действие над конкретным ресурсом в текущем контексте.

Не рассматриваем: внутреннюю криптографию passkeys, полные OAuth/OIDC flows и синтаксис конкретного framework. Модель должна сохраняться при смене протокола.

Что нужно знать заранее ​

Статья «Идентификация» объясняет, как создаётся account и чем identifier отличается от authenticator. Также требуется базовая модель HTTP-запроса.

Результаты обучения ​

После статьи читатель сможет:

  1. разделить identification, authentication, session и authorization;
  2. проследить происхождение principal от credential;
  3. принять authorization decision по action, resource и context;
  4. выбрать 401 или 403 по HTTP semantics;
  5. сравнить session и token models по revocation и blast radius;
  6. написать негативные object-level и tenant-level tests.

Основная модель ​

Проверка контроля одного или нескольких authenticators, связанных с account

создаёт уверенность в том, кто действует сейчас. Авторизация отвечает на другой вопрос: разрешено ли этому principal выполнить это действие над этим ресурсом в этом контексте.

Диаграмма

Наличие user ID в JWT, cookie или header не делает его доверенным. Доверие появляется только после проверки механизма, issuer/audience/expiry или server-side session и связи результата с активным account.

Основные термины ​

ТерминОпределениеНе путать с
AuthenticatorТо, чем subscriber доказывает контроль accountIdentifier или display name
CredentialСтруктура, связывающая identity и authenticatorЛюбая строка из запроса
ClaimantСубъект, пытающийся пройти authenticationУже доверенный principal
PrincipalПроверенная identity в контексте запросаПолный профиль пользователя
SessionОграниченный во времени контекст после authenticationВечная identity
PolicyПравило принятия allow/deny decisionUI visibility
ResourceКонкретный объект или функция, к которым запрошен доступТолько URL path
RevocationДосрочное прекращение доверия к session, token или permissionОбычный expiry

Четыре разных вопроса ​

ЭтапВопросРезультат
IdentificationКакую identity различает система?Account и identifier
AuthenticationКонтролирует ли claimant bound authenticator?Principal и assurance context
SessionКак сохранить результат ограниченное время?Session ID или access token
AuthorizationМожно ли выполнить действие сейчас?Allow/deny + reason/evidence

Смешение этапов создаёт ошибки: email в URL идентифицирует запись, но не аутентифицирует actor; role в token может быть подлинной, но устаревшей; скрытая кнопка выражает UX, но не авторизует API.

End-to-end сценарий: чтение заказа ​

Диаграмма

Критичные свойства:

  • client не задаёт доверенный user_id, role или tenant_id;
  • resource загружается до object-level decision;
  • policy получает action, owner, tenant и context;
  • handler выполняется только после allow;
  • audit не содержит session secret, но позволяет восстановить decision.

Authentication под капотом ​

По NIST SP 800-63B-4 успешная authentication демонстрирует контроль authenticator, связанного с subscriber account. Backend обычно выполняет цепочку:

  1. извлечь credential из определённого transport;
  2. проверить целостность и происхождение;
  3. проверить expiry, audience, nonce или session state по модели;
  4. найти binding с активным account;
  5. учесть revocation и required assurance;
  6. создать минимальный principal для запроса;
  7. зафиксировать outcome без записи секретов.

Session и access token ​

Вопрос не сводится к «JWT или cookie»: cookie — transport/storage mechanism клиента, JWT — формат claims. Возможны разные комбинации.

МодельИсточник текущего состоянияОтзывОсновной риск
Server-side session + opaque IDSession storeПрямой delete/disableДоступность и масштаб store
Opaque access token + introspectionAuthorization serverЦентрализованныйNetwork dependency и caching
Self-contained signed tokenClaims внутри tokenДо expiry сложнееStale permissions и широкий lifetime

Для любой модели определите:

  • короткий срок действия и idle/absolute timeout;
  • rotation при изменении privilege и чувствительных событий;
  • revoke при компрометации, logout или disable account;
  • audience и допустимые consumers;
  • защиту transport/storage;
  • поведение при недоступности verifier;
  • запрет логирования raw secrets.

Authorization decision ​

Полезная сигнатура policy:

text
can(subject, action, resource, context) -> allow | deny + reason

Проверка только роли часто недостаточна. support может читать один tenant, но не другой; владелец заказа может читать его, но refund требует дополнительного permission и свежей authentication.

ts
type Principal = { userId: string; tenantId: string; permissions: string[] };
type Order = { id: string; tenantId: string; ownerId: string };

export function canReadOrder(principal: Principal, order: Order): boolean {
  if (!principal.permissions.includes("orders:read")) return false;
  if (principal.tenantId !== order.tenantId) return false;
  return principal.userId === order.ownerId || principal.permissions.includes("orders:read:any");
}
go
func CanReadOrder(principal Principal, order Order) bool {
	if !principal.HasPermission("orders:read") {
		return false
	}
	if principal.TenantID != order.TenantID {
		return false
	}
	return principal.UserID == order.OwnerID || principal.HasPermission("orders:read:any")
}
php
<?php
function canReadOrder(Principal $principal, Order $order): bool
{
    if (!$principal->hasPermission('orders:read')) {
        return false;
    }
    if ($principal->tenantId !== $order->tenantId) {
        return false;
    }
    return $principal->userId === $order->ownerId
        || $principal->hasPermission('orders:read:any');
}

Ожидаемый результат ​

PrincipalResourceРезультат
Owner, тот же tenant, orders:readСвой заказallow
Owner, другой tenantЧужой tenantdeny
Тот же tenant без orders:readЛюбой заказdeny
Support с orders:read:any в tenantЗаказ tenantallow

Порядок guards выражает deny by default: отсутствие каждого обязательного условия ведёт к deny.

401, 403 и 404 ​

RFC 9110 задаёт semantics:

СитуацияОбычноВажная деталь
Credentials отсутствуют или не подходят401 UnauthorizedОтвет содержит применимый WWW-Authenticate challenge
Credentials действительны, но недостаточны403 ForbiddenПовтор той же authentication не решает запрет
Нужно скрыть существование запрещённого ресурса404 Not Found может использоватьсяВнутренний audit сохраняет реальную причину

Название 401 Unauthorized исторически сбивает с толку: по semantics это прежде всего authentication challenge. Не превращайте любой отказ policy в 401 — клиент может бессмысленно повторять login.

Минимальный воспроизводимый тест ​

Составьте authorization matrix для GET /orders/:id:

text
case                         expected
owner + same tenant          200
other user + same tenant     403 or concealed 404 by policy
owner id + other tenant      404/deny; never leak resource
valid session, missing perm  403
expired session              401 + challenge
no session                   401 + challenge
disabled account             401 or session invalidation policy

Как проверить ​

Тест должен создавать реальные resources с разными owners и tenants. Подмена только URL ID проверяет object-level authorization; подмена tenant_id в body/header не должна менять trusted principal.

Evidence ​

Для deny-записи полезны subject ID, action, resource class/ID, tenant, policy version, reason code, request ID и timestamp. Не записывайте password, session ID, raw access token и sensitive resource contents.

Изменение и отзыв прав ​

Authorization state меняется во времени. Опасный сценарий:

  1. admin удаляет permission;
  2. старый self-contained token ещё содержит permission;
  3. resource server доверяет token до expiry;
  4. пользователь продолжает действие после revoke.

Варианты решения:

  • короткий token lifetime;
  • introspection для критичных действий;
  • session/token version на account;
  • event-driven invalidation с bounded cache;
  • повторная проверка свежего состояния для high-impact action.

Trade-off — latency и availability против окна stale authorization. Он должен быть явным для каждой категории действий.

Failure modes и типичные ошибки ​

  1. Authentication считается authorization. Любой вошедший пользователь получает доступ к resource.
  2. Role приходит от клиента. Недоверенный body/header становится policy input.
  3. Проверяется endpoint, но не object. Возникает IDOR/BOLA.
  4. Tenant взят из URL без сопоставления с principal. Cross-tenant leak.
  5. Deny есть не везде. Новый handler остаётся доступным из-за opt-in middleware.
  6. Token живёт дольше privilege. Revocation не действует до expiry.
  7. Policy cache не имеет версии и bound. Решения остаются stale неопределённо.
  8. Секреты попадают в logs. Detection превращается в источник credential theft.
  9. UI скрывает действие. Прямой API request обходит frontend.
  10. Policy отказалась — сервис разрешил всё. Неопределённый fail-open разрушает trust boundary.

Ограничения и trade-offs ​

РешениеВыигрышЦена или риск
Central policy serviceЕдиные rules и auditLatency, availability, version rollout
Embedded policyБыстро и локальноDrift между сервисами
Self-contained tokenНет lookup на каждый requestStale claims и revocation window
Opaque sessionБыстрый централизованный revokeStateful store
Concealed 404Меньше resource enumerationСложнее support и диагностика
Step-up authСильнее assurance для critical actionFriction и dependency на verifier

Практическое задание ​

Задача: реализуйте и протестируйте policy для одного tenant-scoped resource.

Требования:

  1. principal создаётся только из проверенной session/token;
  2. action задан явно;
  3. resource загружается с owner и tenant;
  4. default — deny;
  5. есть минимум восемь matrix tests, включая cross-tenant и revoked permission;
  6. deny создаёт audit без secrets;
  7. описано поведение при недоступности policy dependency.

Критерии готовности: ни один request parameter не становится доверенным identity attribute; изменение ID, tenant, role и stale token не приводит к allow.

Подсказка

Начните с функции can(subject, action, resource, context). HTTP handler должен лишь получить trusted inputs, вызвать policy и преобразовать decision в response.

Проверка знаний ​

ИНТЕРАКТИВНАЯ ПРОВЕРКА

Проверка знаний

0 / 4
01Запрос не содержит действительных credentials. Какой HTTP-ответ обычно уместен?
02Какие данные нужны для предметного authorization decision?
03Успешная authentication автоматически разрешает чтение любого ресурса.
04Восстановите безопасный путь запроса к заказу.
  1. Загрузить resource и security attributes
  2. Проверить credential/session и получить principal
  3. Выполнить business action
  4. Оценить policy для action и resource
Проверено: 0 из 4

Самопроверка ​

  1. Чем identifier отличается от authenticator и principal?
  2. Почему валидная подпись token не гарантирует актуальные permissions?
  3. Когда 404 вместо 403 оправдан?
  4. Какие данные нельзя писать в authorization audit?
  5. Как проверить object-level authorization?
Ответы и критерии
  1. Identifier обозначает identity, authenticator доказывает контроль, principal — доверенный результат authentication в контексте запроса.
  2. Claims отражают состояние на момент issuance и могут жить после revoke или смены role.
  3. Когда раскрытие существования resource само создаёт риск; внутри причина deny всё равно должна быть наблюдаема.
  4. Raw passwords, session IDs, access/refresh tokens, private keys и sensitive contents.
  5. Создать resources разных owners/tenants и выполнить negative requests с валидной authentication, меняя object ID и context.

Краткое резюме ​

  • Authentication доказывает контроль authenticator; authorization разрешает конкретное действие.
  • Principal формируется только после полной проверки credential/session.
  • Policy оценивает subject, action, resource и context, default — deny.
  • Object-level и tenant-level проверки выполняются на сервере для каждого запроса.
  • Session/token model должна включать expiry, revocation, failure mode и audit.

Следующие шаги ​

Продолжите отдельной статьёй «Авторизация», затем изучите RBAC, ABAC, sessions и JWT и примените их к уязвимостям IDOR/BOLA.

Источники ​

  • NIST SP 800-63-4 — digital identity model и разделение proofing, authentication, federation и authorization inputs.
  • NIST SP 800-63B-4 — authentication assurance, authenticators, session management и lifecycle.
  • RFC 9110 — semantics authentication challenge, 401, 403 и возможность скрыть запрещённый ресурс через 404.
  • OWASP Authorization Cheat Sheet — least privilege, deny by default, server-side checks каждого запроса и authorization testing.