Table of Contents
Введение
Для обеспечения безопасности приложений мы используем такие механизмы как аутентификация и авторизация. Думаю, многие из вас знакомы с этими концепциями и в этой статье мы сфокусируемся на понятие авторизации и связанных с ней моделях контроля доступа.
Определения терминов, которые используются в статье
Важно понимать отличия авторизации от аутентификации:
Аутентификация – процесс подтверждения вашей личности и доказательства того, что вы являетесь непосредственным клиентом системы (посредством пароля, токена или любой другой формы учетных данных).
Авторизация в свою очередь – это механизм, в результате которого запрос к определенному ресурсу системы должен быть разрешен или отклонен.
Субъект доступа – пользователь или процесс, который запрашивает доступ к ресурсу.
Объект доступа – напротив, является ресурсом, к которому запрашивается доступ со стороны субъекта.
Крейт (Crate) – библиотека или исполняемая программа в Rust.
К процессу авторизации относится понятие политики контроля доступа, в соответствии с которой и определяется набор допустимых действий конкретного пользователя (субъекта доступа) над ресурсами системы (объект доступа).
А также модель контроля доступа – общая схема для разграничения доступа посредством пользовательской политики, которую мы выбираем в зависимости от различных факторов и требований к системе.
Модели контроля доступа
Давайте рассмотрим основные модели контроля доступа:
- DAC (Discretionary access-control) – избирательное (дискреционное) управление доступом
Данная парадигма позволяет пользователям самостоятельно передавать право на какие-либо действия над его данными другим участникам системы, для чего используются списки контроля доступа (ACL).
Наиболее распространено применение в случаях, когда пользователи непосредственно владеют некими ресурсами и могут самостоятельно решать кому позволять взаимодействие с ними.
Примером могут служить операционные системы или социальные сети, где люди самостоятельно меняют видимость их контента.
- MAC (Mandatory access-control) – мандатное управление доступом
Была разработана в государственных целях с акцентом на применение в чрезвычайно защищенных системах (например, военных), где и получила наибольшее распространение.
Защита данных основана на метках конфиденциальности (уровень секретности или важности), с помощью которых происходит проверка наличия уровня доступа у субъектов. Характерным также является централизованная выдача прав управляющим органом.
Пожалуй, MAC одна из самых строгих и безопасных моделей, но с этим связана сложность и высокая стоимостьреализации и поддержания инфраструктуры вокруг этого решения (есть множество способов, требующих тщательного планирования).
- RBAC (Role-Based access-control) – управление доступом на основе ролей
Наиболее распространенная и многим известная модель, которая хорошо накладывается на предметные бизнес-области и коррелирует с должностными функциями. Является неким развитием DAC, где привилегии группируются в соответствующие им роли.
Каждый субъект может обладать перечнем ролей, где роль в свою очередь может предоставлять доступ к некому перечню объектов.
Следует отметить, что в рамках RBAC иногда выделяют PBAC (Permission-Based access-control) модель контроля доступа на основе разрешений, когда для каждого ресурса системы выделяется набор действий (например: READ_DOCUMENT
, WRITE_DOCUMENT
, DELETE_DOCUMENT
) и связывают с субъектом через соотношение с ролями, напрямую с пользователем или гибридным подходом – где субъект может обладать ролью и отдельными привилегиями.
- ABAC (Attribute-Based access-control) – управление доступом на основе атрибутов
В данном подходе необходимо ведение специальных политик, которые объединяют атрибуты субъектов и объектов, а решение о допуске предоставляется на основе анализа и сравнительной оценки этих атрибутов.
Это наиболее гибкий из описанных подходов с огромным количеством возможных комбинаций, который позволяет принимать решения на основе таких параметров, как время запроса, местоположение, должность сотрудника и т.п., но требует более детального планирования политик для предотвращения несанкционированного доступа.
Для применения ABAC требуется некий механизм интерпретации политик и некого синтаксического подмножества, что может влечь за собой затраты времени исполнения (в случае динамической реализации) или компиляции (при генерации кода).
Подробнее о некоторых из них можно почитать в материалах OWASP (Open Web Application Security Project) и в документации IBM.
Контроль доступа составляет очень важную часть веб приложений, поскольку необходимо строго соблюдать разграничение доступа к ресурсам и данным в зависимости от привилегий пользователей и в особенности персональным данным, защита которых предусмотрена законодательными аспектами.
Что мы имеем в веб-фреймворках на Rust?
Как правило, для реализации механизмов защиты от несанкционированного доступа в популярных веб-фреймворках (таких, как actix-web, Rocket или tide), используются реализации Middleware
, FromRequest
или Guard
(Filter
в случае warp).
То есть в неком промежуточном ПО, где из запросов можно извлечь данные о субъекте и объекте доступа. Такой подход довольно удобен, поскольку позволят разграничить зоны ответственности.
Это могут быть как библиотечные реализации в виде крейтов, так и пользовательские. Но на текущий момент, предпочтения отдают собственным реализациям, что вероятно связано с небольшим количеством готовых реализаций и спецификой применяемых политик в рамках различных проектов.
casbin-rs
Наиболее обширное production-ready решение с открытым исходным кодом, которое мне удалось найти – это адаптация Casbin (casbin-rs), с внушительным количеством поддерживаемых моделей доступа (заявлены ACL, RBAC, ABAC) и возможностью гибкого изменения политики посредством изменения только лишь конфигурационного файла.
В casbin используется своя мета-модель PERM (Policy, Effect, Request, Matchers) для построения модели доступа, что дает большую гибкость, но привносит затраты на ее интерпретацию и валидацию.
[request_definition]
sub, obj, act
[policy_definition]
sub, obj, act
[policy_effect]
where (p.eft == allow))
[matchers]
r.sub == p.sub && r.obj == p.obj && r.act == p.act
При ее описании можно легко допустить ошибку, в связи с чем был разработан веб-редактор моделей для удобной и корректной модификации
Администрирование привилегий для вашей системы происходит через описание политики (в файле или базе данных), соответствующей формату PERM модели.
p, alice, data1, read
p, bob, data2, write
К сожалению, это вызывает определенное дублирование идентификаторов объектов и субъектов и неочевидность на уровне вызывающего кода.
use *;
async
Такой инструмент определенно заслуживает уважения. Огромное спасибо сообществу, которое вносит свой вклад в его развитие!
Но, как мы можем наблюдать, разработчики учитывают определенные нюансы и отсюда вытекает стремление писать собственные решения из проекта в проект, поскольку требования могут быть детерминированы изначально, а вся предоставляемая гибкость может так и не понадобиться, и следовательно, мы вольны выбирать более узкую и легковесную реализацию, подходящую под наши требования.
Как это было и у меня, когда я взялся за написание backend на Rust. Мне было достаточно модели PBAC и исходя из своего опыта разработки веб-приложений, в большинстве типовых проектов достаточно моделей ACL/RBAC.
В связи с чем я пришел к идее реализации и вынесения собственного решения в качестве отдельного крейта с открытым исходным кодом: actix-web-grants.
actix-web-grants
Основная идея проекта состоит в использовании встроенной middleware для получения привилегий пользователей из запроса и указанию необходимых разрешений у пользователей непосредственно на ваших эндпоинтах.
Это довольно легковесный крейт с простым подключением, с использованием которого можно, как минимум, применять следующие модели: списки доступа(ACL), управление доступом на основе ролей или разрешений(RBAC/PBAC).
Таким образом, нам достаточно реализовать функцию получения привилегий:
// Sample application with grant protection based on extracting by your custom function
async
async
Данный подход добавляет гибкости и позволяет нам реализовывать авторизацию вне зависимости от способов аутентификации и хранения привилегий пользователей: это может быть JWT-токен, база данных, промежуточный кэш или любое другое решение.
После чего мы можем расставлять ограничения непосредственно над нашими ресурсами:
use ;
async
Возможность влиять на политику доступа напрямую в коде является отличительной частью actix-web-grants, снижая дублирование объектов доступа и предоставляя нам наглядную информацию о необходимых привилегиях.
Для полноты картины, написаны минимальные примеры приложений с идентичным профилем использования и проведены замеры производительности процесса авторизации (на базе wrk) для удовлетворения собственного интереса.
Примеры написаны с упрощенной реализацией модели RBAC для двух тест-кейсов авторизации: запрос к ресурсу разрешен и отклонен, в соответствие с наличием необходимых ролей. Для аутентификации использовались заглушки. Весь код опубликован на GitHub: actix-web-authz-benchmark (больше примеров всегда можно найти на страницах самих проектов).
Результаты бенчмарка можете наблюдать в таблице:
Benchmark | casbin-rs | actix-web-grants | ||
Latency | Req/Sec | Latency | Req/Sec | |
Allowed Endpoint | 6.18 ms | 16.27k | 4.41 ms | 22.69k |
Denied Endpoint | 6.70 ms | 14.98k | 4.94 ms | 20.23k |
rustc: v1.52.0 (stable); CPU: 2,6 GHz 6-Core Intel Core i7; RAM: 16 GB
Таким образом, мы видим, что actix-web-grants позволяет более просто интегрировать и администрировать политики доступа над конечным точками (endpoint), при этом не уступает в производительности по сравнению с casbin-rs.
Post Scriptum
Данная библиотека пока не имеет в своём арсенале интеграций с множеством веб-фреймворков, но у меня есть планы по вынесению некоторых абстракций и написанию модулей под другие фреймворки, внесению некоторых улучшений (например, возможность наследования ролей и поддержки пользовательских типов). Буду рад любым предложениям и вкладу!