Задача
Разработать механизм, проверяющий поступивший в web-приложение запрос на право выполнение команды web-приложения.
Требования
Для пользователя, отправляющего запрос на выполнение команды, проверка прав должна проходить прозрачно - он не должен при каждом запросе вводить пароли для подтверждения своих прав. Это требование подразумевает, что необходимые данные, подтверждающие права пользователя, могут безопасно храниться на локальном компьютере (например, в кукисах), и, по необходимости, отправляться web-приложению автоматически.
Общая идея решения
- Чтобы получить право на выполнение команд web-приложения, пользователь должен сначала авторизоваться (например, по OAuth). После этого web-приложение выделяет ему индивидуальный ключ, который сохраняется в профиле пользователя
- Действие ключа ограничено. Это делается из соображений безопасности. После того, как действие ключа закончится, пользователю нужно заново запросить ключ у web-приложения (заново пройти авторизацию), чтобы получить новый действующий ключ.
- Ограничение действия ключа вводится по времени. Время начала действия ключа - немедленное, т.е. ключ начинает действовать сразу после его генерации.
- Параметр ограничения ключа про времени, имеет несколько единиц детализации. Это - 'минута', 'час', 'сутки'. Такого деления будет достаточно для большинства web-приложений.
Генерация ключа и списка контрольных точек
- Генерация ключа выполняется шифрованием ssh224 входной строки. Входная строка должна содержать: а) идентификаторы пользователя, б) информацию о параметре, ограничивающем действие ключа - время действия ключа.
- Идентификаторы пользователя - один или более текстовых фрагментов, уникальных только для одного пользователя, например ['user_id', 'user_email']. Идентификатор пользователя может быть не предусмотрен web-приложением. В этом случае сгенерированный секретный ключ будет одинаковым для всех пользователей
- Параметр времени, ограничивающие действия ключа - строковое выражение времени генерации ключа. Если web-приложением парметр не предусмотрен, для каждого пользователя будет сгенерирован уникальный бессрочный ключ (на основании его идентификатров).
- Входная строка для генерации ключа является результатом "сцепления" заданных идентификаторов пользователя и времени генерации ключа.
- Идентификаторы пользователя, время генерации ключа и порядок их "сцепления", образующий входную строку для генерации ключа, являются основой защиты описываемого в данной публикации метода валидации пользователя
Проверка ключа
Проверка действительности ключа проверяется по списку контрольных точек. Если ключ найден в списке - он действителен, если нет - пользователю необходимо получить новый ключ.
Формирование списка контрольных точек выполняется сразу после решения web-приложения выделить пользователю секретный ключ, например, после успешного прохождения пользователем OAuth авторизации. Список контрольных точек сохраняется в web-приложении в профиле пользователя.
Исходными данными для формирования списка контрольных точек являются а) идентификаторы пользователя, б) текущее время, сразу после успешного прохождения авторизации, в) единица детализации, г) интервал времени ограничивающей время действия ключа, начиная с текущего момента
Значение первой контрольной точки в списке генерируется, исходя из текущего времени. Далее, значение времени увеличивается на одну единицу детализации, рассчитывается следующая контрольная точка и добавляется в список контрльных точек. Каждое новое значение параметра ограничения проверяется на принадлежность к заданному интервалу. Как только значение превышает интервал - дальнейшие вычисления прекращаются.
Алгоритм валидации пользователя
- Генерация ключа
Право на выполнение команды web-приложения получают только пользователи с сдействующим ключом. Чтобы получить ключ нужно успешно авторизоваться через OAuth. После успешной авторизации web-приложение генерирует список контрольных точек и сохраняет его в профиле пользователя.
Генерация ключа для сравнения происходит, когда пользователь отправляет запрос на выполнение операции. Запрос содержит идентификаторы пользователя. Добавляем к нему (в заданном порядке) время поступления запроса и по полученной строке генерируем секретный ключ.
Сравниваем ключ со списком контрольных точек: если ключ в списке - запрашиваемая операция выполняется, если нет - web-приложение предлагает пользователю получить новый ключ - пройти авторизацию.
Такой алгоритм повышает безопасность, поскольку позволяет не передавать за пределы web-приложения информацию о секретных ключах: список контрольных точек хранится внутри приложения, а секретный ключ вообще нигде не хранится.
Если пользователь отправляет web-приложению запрос на выполнение команды без секретного ключа - приложение отправляет его на авторизацию.
Дополнительное условие генерации
При генерации ключа и списка контрольных точек следует учитывать одну особенность реализации алгоритма. Для того, чтобы обеспечить разумную длину списка контрольных точек и одновременно действительность секретного ключа в течение всего заданного интервала принимается следующее соглашение - время начала генерации секретного ключа и или списка контрольных точек округляется в меньшую сторону до ближайшего целого значения в соотвествии с выбранным масштабом детализации.
Реализация
Описываемый алгоритм реализован на python в виде класса. Код доступен здесь
Создание экземпляра генератора
k=KeyGenerator(time_shift=2, salt_position=0, text_array=[], time_point="hour")
- time_shift - время действия секретного ключа. Задается целым числом в абстрактрых единицах. Перевод в физические единицы детализации (минута, час, день) определяется значением параметра time_point
- salt_position - позиция времени генерации ключа в списке идентификаторов пользователя
- text_array - список ищентификаторов пользователя
- time_point - масшаб детализации списка контрольных точек
Свойства и методы экземпляра класса
- time_point - свойство экремпляра в котором хранится список временных контрольных точек (в строковом представлении)
- getEncryptPoints() - метод генерирует и возвращает список контрольных точек
- createUserKey() - генерирует и возвращает секретный ключ пользователя
Пример реализации: Идентификаторами пользователя являются 2 параметра user_id и user_email. После прохождения авторизации ему нужно присвоить секретный ключ, который будет в течение 15 минут подтверждать его право на соверщение авторизации.
from secret_key import KeyGenerator
#...
# user_id и user_email пользователя после успешной авторизации
k=KeyGenerator(15, 2, [user_email, user_id], time_point='minute')
key_points=k.getEncryptPoints()
# сохранение key_points в профиле пользователя
#...
# запрос от пользователя на выполнение команды
k=KeyGenerator(15, 2, [user_email, user_id], time_point='minute')
secret_key=k.createUserKey()
# получение из профиля список контрольных точек key_points и выполняем проверку
if secret_key in key_points:
# разрешаем выполнение операции
# генерируем новый список ключей и сохраняем в профиле пользователя (опционально)
else:
# направление на авторизацию