Статьи

Простой, безопасный контроль доступа на основе ролей (RBAC) для API REST

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

Разрешения на набор ролей

Управление разрешениями для пользователя может быть утомительной задачей, когда в нее вовлечено много пользователей. По мере добавления пользователей в систему поддержание прав пользователей становится все труднее и более подвержено ошибкам. Неправильное назначение разрешений может блокировать доступ пользователей к требуемым системам или, что еще хуже, разрешать неавторизованным пользователям доступ к закрытым областям или выполнять рискованные операции.

В этой статье я расскажу о том, как элегантно управлять доступом к приложениям RESTful. Существует много различных моделей управления доступом, таких как управление доступом на основе ролей (RBAC) и дискреционное управление доступом (DAC). Хотя принципы, описанные в документе, могут применяться к различным моделям, я выбрал RBAC в качестве справочного, поскольку он широко принят и очень интуитивен.

Немного о ролях

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

Разрешения на набор ролей

Управление разрешениями для пользователя может быть утомительной задачей, когда в нее вовлечено много пользователей. По мере добавления пользователей в систему поддержание прав пользователей становится все труднее и более подвержено ошибкам. Неправильное назначение разрешений может блокировать доступ пользователей к требуемым системам или, что еще хуже, разрешать неавторизованным пользователям доступ к закрытым областям или выполнять рискованные операции.

Просмотр действий пользователей обычно дает только ограниченное количество действий, которые выполняют пользователи (например, чтение данных, отправка форм). При более внимательном рассмотрении этих действий пользователя можно обнаружить, что некоторые действия, как правило, сочетаются друг с другом. Это означает, что пользователи, которые выполняют действие A, обычно также выполняют действие B. Например, чтение и обновление отчетов или удаление и добавление учетных записей. Затем их можно объединить в «роли», такие как «Редактор» или «Администратор учетной записи». Обратите внимание, что роли не обязательно связаны с должностями или организационной структурой, а скорее отражают соответствующие действия пользователя. Как только роли правильно определены и назначены каждому пользователю, разрешения могут быть назначены ролям, а не пользователям. Управление разрешениями для небольшого числа ролей — намного более легкая задача.

Как всегда, иллюстрация имеет большое значение:

Вот набор пользователей и их назначенные разрешения, связанные напрямую без ролей:

И вот, точно такой же набор пользователей и разрешений , организованный с ролями:

Таким образом, вы можете ясно видеть, как роли делают управление разрешениями намного проще!

Группы Bundle Пользователи

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

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

Реализация ролей — делай и не делай

Никогда не соединяйте действия и детали авторизации

Во многих системах разработчики ограничивают доступ к определенной операции, указывая разрешения непосредственно в методе реализации. Да, в коде! Как правило, проверка роли добавляется в защищенный метод, часто путем его аннотирования. Вот пример из кода на основе Spring Security:

@PreAuthorize("hasRole('Editor')")
public void update_order(Order order);

Это очень распространенная практика, используемая на разных языках и в разных системах. Хотя это очень легко реализовать, к сожалению, оно создает нежелательную связь между требуемой ролью и фактической реализацией действия. Представьте себе десятки методов, снабженных жестко закодированными именами ролей. Отслеживание эффективных разрешений каждой роли становится настолько сложным, что вы почти наверняка можете рассчитывать на наличие неточной или устаревшей документации или, что еще хуже, неизвестных неуправляемых разрешений, разбросанных по вашему приложению.

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

Как избежать сцепления?

Лучшим подходом было бы сначала извлечь список возможных действий из кода, который должен обрабатываться внешним механизмом авторизации (объяснено ниже). Затем мы можем сделать код не осведомленным о ролях или любых других деталях авторизации и просто спросить, имеет ли текущий пользователь (как бы его ни извлекали) необходимые разрешения (где бы он ни был определен) для выполнения определенного метода.

Это позволило бы нам использовать общую аннотацию, такую ​​как эта:

@Secured
public void update_order(Order order);

Сопоставление ролей и разрешений (т. Е. Разрешения на выполнение определенного действия) теперь можно выполнить в файле конфигурации, который легко настраивается клиентами!

Например, рассмотрим этот файл role_config.yaml:

order_manager:
  - 'create_order'
  - 'view_order'
  - 'delete_order'
  - 'update_order'
order_inspector: 
  - 'viewer_order'

@securedОбертка теперь можно оценить , если текущий пользователь имеет право выполнить «update_order» на основе данного файла конфигурации. В этом случае это будет означать, что текущему пользователю должна быть назначена роль «order_manager», которая теперь понятна и легко настраивается. Тем не менее, механизм авторизации должен каким-то образом знать, как сопоставить каждое разрешение с определенным методом в коде, и кто-то должен выполнить некоторую работу и задокументировать все доступные методы (например, create_order, view_order и т. Д.). Это решено (почти) волшебным образом ниже.

Отдельные проблемы — авторизовать извне

Теперь, когда код реализации метода не включает подробности авторизации, всю логику авторизации можно перенести в отдельный независимый модуль. Используя общий заголовок (например, аннотацию «безопасный»), мы разрешаем модифицировать весь механизм авторизации, не затрагивая код приложения. Например, можно было бы реализовать «защищенный» в качестве проверки роли, но также можно было бы использовать списки контроля доступа (ACL). Например, оценка, если текущий пользователь указан в списке ACL заказа. Другое решение может состоять в том, чтобы использовать oauth, спрашивая стороннее лицо (например, Facebook), разрешено ли пользователю выполнять это действие или нет.

ОТДЫХ — лучший

Действие извлечения — из коробки

REST определенно лучше или, по крайней мере, легче всего подходит для этой модели. Системы RESTful (спроектированные должным образом) уже предоставляют ресурсы и методы через стандартный API на основе HTTP, ресурсы идентифицируются с помощью URI, а методы моделируются с помощью глаголов HTTP (например, GET, PUT).

Например, OST http://www.domain.com/bookingsсоздаст новое бронирование и GET http://www.domain.com/orders/12345вернет детали заказа № 12345. Это означает, что извлечение описанных выше действий готово прямо из коробки!

Запросить шлюз

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

Запросы также являются инструментами контроля доступа

Службы REST обрабатывают входящие запросы, что означает, что информация, найденная в запросах, может использоваться для принятия решений по управлению доступом. Некоторые полезные детали:

  • Источник запроса — позволяет блокировать запросы, отправленные с неизвестных IP-адресов или подсетей.
  • Заголовки — в заголовках может быть передано много интересных деталей, таких как учетные данные пользователя, которые открывают дверь для полноценного процесса аутентификации / авторизации.
  • Целевая конечная точка — как указано в URI запроса (например, «секреты» в « http://domain.com/secrets/ »). Доступ может быть ограничен только подмножеством конечных точек приложения, в зависимости от других условий. Например, хотя конечная точка «версия» открыта для всех, конечная точка «секреты» открыта только для аутентифицированных пользователей.
  • Целевой метод — как представлено глаголом HTTP (например, DELETE), это означает, что можно передавать или блокировать запросы на основе вызываемого метода.

Соедини все вместе — используя REST для контроля доступа

Помните простой файл конфигурации ролей к разрешениям, приведенный выше? Это выглядело довольно элегантно, но повлекло за собой некоторую работу за кулисами (не показано в этом документе), такую ​​как получение списка всех методов, которые может вызвать пользователь, и сопоставление каждого разрешения для определенного метода с этим именем. Не смешно.

Это теперь решено!

Все доступные ресурсы предоставляются через REST URI, которые вместе с глаголами HTTP могут охватывать все действия, которые можно выполнить и которые необходимо защитить. В приведенном ниже примере настроены 3 роли:

  • order_manager — может просматривать, создавать, обновлять и удалять заказы
  • order_editor — может просматривать, создавать и обновлять заказы, но не удалять их
  • order_inspector — может только просматривать заказы

order_manager:
  '/orders':
    - 'GET'
    - 'POST'
    - 'PUT'
    - 'DELETE'
order_editor:
  '/orders':
    - 'GET'
    - 'POST'
    - 'PUT'
order_inspector:
  '/orders':
    - 'GET'

Итак, теперь мы знаем, что системы RESTful естественным образом подходят для контроля доступа. Обрабатывая входящие запросы, служба REST может получить ценную информацию, которая может быть передана в отдельный модуль для выполнения аутентификации и авторизации. Если пользователь авторизован для выполнения запрошенного метода на целевом ресурсе, обработка запроса может продолжаться. В противном случае, это подходящее место, чтобы запретить дальнейший доступ до того, как будет достигнут какой-либо внутренний код приложения.

В моем следующем посте я объясню, как мы реализуем эту модель RBAC в Cloudify, поэтому зайдите позже.