Авторизация действий в веб-приложениях #
Васильев Андрей Михайлович, 2022
Версии презентации
Идентификация, аутентификация, авторизация #
- Идентификация — процедура, в результате выполнения которой для субъекта идентификации выявляется его идентификатор, однозначно определяющий этого субъекта в информационной системе
- Аутентификация — это процедура проверки подлинности субъекта, например путём сравнения введённого им пароля с паролём, сохранённым в базе данных
- Авторизация — предоставление субъекту прав на выполнение определённых действий
Что требует авторизации #
Авторизовать необходимо любое действие, которое пользователь может выполнить с приложением:
- Просмотр данных
- Редактирование данных
- Удаление данных
- Добавление данных
То есть необходимо проверять возможность выполнения любой ПРУД-операции
Авторизация во время выполнения HTTP-запроса #
Как и в случае с аутентификацией, авторизация должна происходить при каждом отдельном запросе от клиента к серверу
Обычно выполняется сразу после аутентификации, используя результаты последней
Может выполняться на следующих уровнях:
- Во время маршрутизации запроса
- Внутри обработчика HTTP-запроса
- Внутри операции над базой данных
Авторизация в рамках пользовательского интерфейса #
Потенциально можно реализовать две стратегии внутри организации интерфейса
- Предоставлять всем пользователям все элементы управления
- Предоставлять пользователям доступ только для действий, которые они могут выполнить
Для узкоспециализированных систем со сложными подходами к авторизации можно использовать первый подход, но для большинства пользовательских систем следует использовать второй подход
- Если пользователю не предоставлять возможность выполнить запрещённое действие, то ему будет гораздо сложнее выполнить соответствующий запрос
- Отсутствие ссылки на страницу не является защитой от вызова данной ссылки пользователем, авторизация на уровне HTTP-обработчика должен оставаться
Схемы предоставления прав доступа #
Права доступа всегда относятся к конкретному субъекту, но существует несколько подходов к установлению связи между субъектом и правами
Можно выделить следующие общие подходы предоставления прав доступа:
- Прямая выдача прав на выполнение операций пользователю
- Выдача прав роли и ассоциация пользователя с одной ролью
- Выдача прав группам и включение пользователя в несколько групп
Выдача прав пользователям #
В рамках приложения выделяются операции, требующие авторизацию
Затем выполняется выдача прав одним из возможных способов:
- Для каждой операции формируется список пользователей, которые могут ей воспользоваться
- Для каждого пользователя формируется список операций, которые данный пользователь может выполнить
- При появлении нового пользователя или новой операции необходимо обновить списки предоставления прав доступа
- При удалении элементов также необходимо удалить и соответствующие разрешения
Выдача прав доступа ролям #
В рамках приложения выделяется концепция «роли», которая может выполнять разумный связный набор операций внутри предметной области приложения
Использование роли является логичным продолжением использования сценариев использования и пользовательских историй для описания функциональности приложения
- Выделяются операции, требующие авторизации
- Для каждой роли определяется возможность выполнения операций
Каждому пользователю системы назначается роль
Выдача прав группам #
Данная схема похожа на схему выдачи прав ролям, однако она более сложная:
- Каждый пользователь может принадлежать нескольким группам
- Между группами может быть сложная иерархическая связь, то есть одни группы могут входить в другие группы
- Потенциально группам можно не только явно разрешать, но и явно запрещать доступ к операциям
Название «группы» и «роли» могут трактоваться внутри разных информационных систем по-разному. Использование разных терминов предлагается для использованию в рамках курса
Выделения прав в системе #
Права в системе могут быть прописаны либо статически, либо динамически
- При статической организации трудно поддержать большое количество различных ролей внутри приложения
- Для поддержки динамического распределения ролей необходимо предоставлять отдельный графический интерфейс, доступ к функциям которого тоже надо авторизовать
Для систем небольшого размера достаточно статического определения прав доступа. Для систем среднего и большего размера необходимо использовать системы динамического распределения прав
Ввиду востребованности и требований обеспечения безопасности системы задачу по выделению прав выносят в отдельную систему с которой взаимодействуют по ряду протоколов: SAML, OpenID Connect и т.д.
OpenID Connect #
Протокол OpenID Connect является расширением протокола OAuth 2.0, предоставляющий слой для идентификации пользователя после авторизации на идентификации провайдера
Протокол поддерживает следующие потоки авторизации:
- Неявный поток авторизации, подходит для одностраничных приложений
- Базовый поток авторизации, подходит для серверных приложений
- Поток предоставления доступа через пароль, для случая отсутствия доступа к веб-браузеру
- Поток предоставления учётных данных, для авторизации действий машин
Рассмотрим первые два потока, предназначенные для пользовательских приложений
Неявный поток авторизации #
В неявном потоке авторизации у пользователя нет возможности скрыть какие-то данные, так как состояние веб-приложения всегда можно исследовать средствами разработки
- Передача данных осуществляется по зашифрованному соединению, поэтому данный поток безопасен
- Для привязки ключа используется набор из доменного имени, ключа клиента и асимметричного шифрования на стороне провайдера идентификации
- Публичная часть ключа доступна клиенту, с её помощью он может проверить, что переданный ключ верен
Базовый поток авторизации #
В рамках данного потока взаимодействие провайдера идентификации включает взаимодействие с серверным приложением
Процесс идентификации и аутентификации #
- Сессионный токен удобно оформить в виде JWT-токена, чтобы уменьшить объём данных, которые надо сохранять на сервере для выполнения авторизации
- В рамках такой схемы не стоит выдавать JWT-токен на длительное время, т.к. его будет невозможно отозвать
- Также стоит продумать схему автоматической замены JWT-токена при приближении срока его истечения
Выбор подхода к авторизации #
В рамках учебных приложений будем использовать подход с определением ролей
- Минимальный объём по настройке прав — только указание роли
- Не требуется создания дополнительного интерфейса для управления ролями
- Операции, запрещённые пользователю к исполнению, не должны показываться в пользовательском интерфейсе
Для предоставления ролей будет достаточным отредактировать хранилище напрямую
Графический интерфейс #
- При создании графического интерфейса доступ к нему должен быть обеспечен только пользователям с ролью «администратор»
- Для создания первого администратора можно
- Создать специальную команду по редактированию данных в хранилище
- Назначить первого созданного пользователя администратором
Поток авторизации #
При выполнении операций и при показе пользовательского интерфейса необходимо проверить разрешения для текущего аутентифицированного пользователя
- Выполнять отдельный запрос к подсистеме авторизации при каждом действии
- Формировать полный список разрешений, который может быть использован внутри слоя обработчиков и внутри уровня представления
При использовании второго подхода можно логику по вычислению списка разрешений делегировать фильтру и сохранять их в контекст обработчика
Реализация ролей #
Внутри приложения для реализации ролей можно использовать следующий подход:
- Сформировать список действий, для которых необходимо обеспечить авторизацию.
- Сформировать класс с полями для сохранения логических значений, которые показывают разрешено или запрещено данное действие.
- Создать объекты данного класса, содержащие корректные поля для каждой роли.
- Данные объекты можно сделать константными внутри контекста приложения
- Содержимое данных классов можно разместить в хранилище приложения
data class RolePermissions(
val listEntries: Boolean,
val showEntry: Boolean,
val addEntry: Boolean,
val editEntry: Boolean,
val deleteEntry: Boolean,
)
val AnonymousRole = RolePermissions(
listEntries = true,
showEntry = true,
addEntry = false,
editEntry = false,
deleteEntry = false,
)
val GenericUser = AnonymousRole.copy(
addEntry = true,
)
val Administrator = GenericUser.copy(
editEntry = true,
deleteEntry = true,
)
Фактор владения данными #
Представленная схема обеспечивает доступ ко всем функциям «по умолчанию», то есть обычный пользователь не может удалять или редактировать записи
Значимым исключением из данной практики является фактор владения объектом. Если пользователь является владельцем, то он может выполнять расширенный набор действий
Для определения права владения:
- Необходимо получить запрашиваемый объект
- Сопоставить аутентифицированного пользователя и объект, выявить факт владения
- Использовать информацию о базовых разрешениях и факторе владения
Проверку на владение можно проводить на уровне HTTP-обработчика или операции над данными