коротко

Аутентификация отвечает «кто ты», авторизация — «что тебе можно»; это разные вещи, не путайте (про первую — в записи про OAuth2 и JWT). Авторизацию проверяют не одну модель: RBAC — права через роли (роль «менеджер» → набор прав), просто, но грубо. ABAC — права через атрибуты (кто, что, в каком контексте: «свой департамент», «в рабочее время»), гибко, но сложно отлаживать. Row-level security — доступ к конкретным строкам, а не только к эндпоинту: не «можно ли смотреть брони», а «можно ли смотреть эту бронь». Главное для аналитика: авторизацию нельзя проверять только в UI или на входе в API — её надо доводить до уровня данных, иначе случается горизонтальная эскалация (подменил id в запросе — увидел чужое). Принцип least privilege: каждому ровно столько прав, сколько нужно для работы, и ни граммом больше.

Однажды я тестировал кабинет, где сотрудник видел свои брони переговорок по адресу вида /bookings?userId=42. Из любопытства я поменял 42 на 43 — и спокойно открыл чужие брони: с кем человек встречается, когда, в какой комнате. Сервер проверил, что я залогинен, проверил, что у меня есть право «смотреть брони» — и на этом успокоился. Он ни разу не спросил, мои ли это конкретные брони. Кнопки «чужие брони» в интерфейсе не было, поэтому все думали, что доступа нет. Но интерфейс — это не охрана, это вывеска. С тех пор первый вопрос, который я задаю на любой ручке со списком: «а кто проверяет, что эти строки принадлежат именно этому пользователю?» — и очень часто ответом оказывается тишина.

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

Две темы, которые постоянно сливают в одну. Аутентификация (authentication) — это про личность: ты докажи, что ты — это ты. Логин-пароль, токен, одноразовый код. Подробно — в записи про OAuth2 и JWT, здесь её не дублирую. Авторизация (authorization) — это следующий шаг: личность мы установили, теперь решаем, что этой личности позволено. Можно ли ей читать этот заказ, отменять эту бронь, видеть зарплаты.

Запомнить разницу просто: аутентификация — это паспорт на входе, авторизация — это список, в какие двери внутри здания этот паспорт открывает. Сначала первое, потом второе; перепутать их — значит впустить человека и забыть проверить, куда он пошёл. Эта запись — целиком про второе.

Авторизация — это не одна галочка

Частая ошибка постановки: «проверьте права» — и всё. Но «права» — это слой. Есть право зайти на эндпоинт («можно ли вообще смотреть брони»), а есть право на конкретный объект («можно ли смотреть эту бронь»). Грубая проверка ловит первое и пропускает второе. Аналитик обязан в требованиях развести эти два уровня явно, иначе разработчик закроет верхний и решит, что готово.

RBAC: права через роли

RBAC (Role-Based Access Control) — самая распространённая модель. Идея в одном промежуточном звене: права привязывают не к человеку напрямую, а к роли, а человеку выдают роль. «Менеджер» — это набор прав; Иванова сделали менеджером — Иванов получил весь набор. Уволился, сменил отдел — сняли роль, права ушли вместе с ней.

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

Удобно описывать RBAC матрицей «роль × право» — это рабочий артефакт, который аналитик кладёт прямо в спеку. Вот такая матрица для сервиса бронирования переговорок (тот самый, что в записи про пример SRS):

Право РольСотрудникТимлидОфис-менеджерАдмин
Создать броньдададада
Смотреть свои бронидададада
Смотреть брони командынетдадада
Отменить чужую броньнетда (своей команды)дада
Управлять переговоркаминетнетдада
Управлять роляминетнетнетда

Обратите внимание на строку «отменить чужую бронь → да (своей команды)»: матрица RBAC даёт право на действие, но «своей команды» — это уже не про роль, а про данные. Чистый RBAC такое выразить не может — и здесь он упирается в потолок, о который мы сейчас и стукнемся.

ABAC: права через атрибуты

ABAC (Attribute-Based Access Control) решает то, что RBAC не вытягивает. Здесь право вычисляется не из роли, а из атрибутов, которые сводят в правило: атрибуты субъекта (кто: его отдел, должность, команда), объекта (что: владелец брони, её статус), действия (что хочет сделать) и контекста (когда, откуда: рабочее время, IP из офисной сети). Правило звучит как фраза: «тимлид может отменить бронь, если команда брони совпадает с его командой и бронь ещё не началась».

Это гибко: вы выражаете политику, которую в матрице ролей не нарисовать, и не плодите роли вроде «тимлид_команды_маркетинга_в_рабочее_время». Цена — сложность. Правил становится много, они пересекаются, и на вопрос «почему Петров не смог отменить бронь» уже нет простого ответа «нет роли» — надо прогонять весь набор условий и смотреть, какое сработало. Отладка и аудит ABAC ощутимо дороже.

RBAC и ABAC — не выбор «или-или»

На практике их часто комбинируют: грубый слой на RBAC (роль решает, какие действия в принципе доступны), тонкий слой на ABAC (атрибуты решают, над какими объектами). В нашей матрице это видно: роль «тимлид» открывает право «отменить чужую бронь», а атрибутное правило «команда совпадает» сужает его до своей команды. Начинать почти всегда стоит с RBAC и добавлять атрибуты только там, где роль реально не справляется, — иначе утонете в правилах на ровном месте.

Row-level security: доступ до строки

Вот ядро всей темы. И RBAC, и грубая проверка на входе в API отвечают на вопрос «можно ли тебе эта операция». Но почти всегда настоящий вопрос — «можно ли тебе эта строка». Право «смотреть брони» есть у всех сотрудников. Это не значит, что каждый может смотреть брони каждого. Row-level security (доступ на уровне строк) — это проверка не эндпоинта, а конкретной записи: эта бронь — твоя? Этот заказ — твоего клиента?

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

Правило row-level из NFR того же сервиса бронирования (в SRS оно записано как «сотрудник видит только свои брони») в требованиях формулируют так, чтобы его нельзя было прочитать двояко:

Правило доступа RL-1 (чтение списка броней):
  сотрудник           → видит ТОЛЬКО брони, где booking.team_id = user.team_id
  тимлид              → видит брони своей команды (team_id совпадает)
  офис-менеджер/админ → видят все брони

Проверка делается НА СЕРВЕРЕ при выборке данных,
а не фильтрацией в UI. Запрос GET /bookings/{id}
с чужим id обязан вернуть 404 (не 403).

Последняя строка — тонкий момент на стыке с ошибками в API. Если на чужую бронь вернуть 403 Forbidden, вы подтвердили её существование: «такая бронь есть, просто не твоя». Это утечка факта. Когда сам факт существования объекта чувствителен, на чужой ресурс отвечают 404 Not Found — «нет такой брони» — и атакующий не отличит «не существует» от «не твоя». Какой код выбрать — решение постановки, но молчать о нём нельзя.

Фильтр в UI — это не авторизация

Самая частая дыра: кнопку «чужие данные» спрятали в интерфейсе и решили, что доступа нет. Но клиент полностью под контролем пользователя — запрос отправляют напрямую, минуя вашу форму (почему именно так — в записи про безопасность данных и PII). Если сервер не проверяет принадлежность строки сам, спрятанная кнопка не защищает ничего. Авторизацию доводят до уровня данных на сервере, всегда.

Least privilege и где это живёт в требованиях

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

Где это живёт у аналитика. Авторизация — это не «бэк сам разберётся», это требования, и их источник — постановка. Конкретно: матрица «роль × право» в спеке или SRS; явные правила row-level для каждой ручки, отдающей списки или объекты («кто какие строки видит»); решение про 403 vs 404 на чужой ресурс. Безопасник в этой работе — полноправный стейкхолдер: модель доступа стоит согласовывать с ним так же, как функциональные требования — с продактом.

Откуда это взялось

Ролевую модель формализовали Дэвид Феррайоло и Рик Кун в 1992 году — до этого доступ раздавали кто во что горазд. В 2004 году RBAC закрепили как национальный стандарт США ANSI INCITS 359-2004 (на базе работ NIST), и роли стали индустриальной нормой. Атрибутную модель NIST описал отдельным руководством — SP 800-162 «Guide to Attribute Based Access Control», вышедшим в январе 2014 года; характерно, что среди авторов — те же Феррайоло и Кун, то есть ABAC рос не на смену RBAC, а как его более гибкое продолжение для случаев, где ролей не хватает.

Как это спрашивают на собесе

Типичные формулировки: «Чем аутентификация отличается от авторизации?», «Что такое RBAC и ABAC, когда что?», «Как сделать, чтобы сотрудник видел только свои данные?», «Что такое горизонтальная эскалация?», «На чужой ресурс вернуть 403 или 404?». Сильный ответ: authn — кто ты, authz — что можно; RBAC прост и груб (права через роли), ABAC гибок и сложен (права через атрибуты, часто поверх RBAC); ключевое — доводить проверку до уровня строк на сервере, иначе подмена id вскрывает чужие данные; least privilege по умолчанию. Упоминание про 404 вместо 403 и про то, что фильтр в UI не является защитой, — маркеры зрелости.

Частые вопросы

Чем RBAC отличается от ABAC простыми словами?

RBAC выдаёт права через роли: есть роль «менеджер» с фиксированным набором прав, человеку дают роль — он получает весь набор. Это просто настраивать и легко аудировать, но грубо: всё, что не укладывается в роль, выразить нельзя. ABAC выдаёт права через атрибуты и правила: «можно, если отдел совпадает и сейчас рабочее время». Это гибко — описывает тонкие политики, — но сложнее в отладке и аудите, потому что на вопрос «почему доступа нет» приходится прогонять весь набор условий. На практике их комбинируют: роли решают, какие действия доступны, атрибуты — над какими объектами.

Что такое row-level security и зачем она нужна?

Это проверка доступа к конкретным строкам данных, а не только к эндпоинту. Право «смотреть брони» может быть у всех сотрудников, но это не значит, что каждый вправе видеть брони каждого. Row-level security проверяет на сервере при выдаче каждой записи, что она принадлежит запрашивающему («эта бронь — твоей команды?»). Без неё возникает горизонтальная эскалация: пользователь подменяет чужой id в запросе и получает чужие данные своего же уровня. Защита от этого — требование, которое аналитик описывает по каждой ручке, отдающей списки или объекты.

Почему нельзя проверять права только в интерфейсе?

Потому что интерфейс не охрана, а вывеска. Спрятать кнопку «чужие данные» в UI не защищает ничего: код в браузере под полным контролем пользователя, и запрос на сервер можно отправить напрямую, минуя вашу форму. Если сервер при этом не проверяет принадлежность строки сам, данные утекут. Авторизацию доводят до уровня данных на сервере — он при каждой выдаче решает, можно ли этому пользователю именно эту запись. UI лишь прячет недоступное для удобства, а настоящая проверка всегда серверная.