Json Web Token И Скользящий Срок Действия В Веб-Приложении

В веб-приложениях до сих пор наиболее распространенным методом аутентификации было использование файлов cookie, которые хранят идентификатор сеанса сервера и имеют срок действия.

При этом есть возможность автоматически продлить эту дату при следующем обращении пользователя к серверу.

Этот подход называется скользящим сроком погашения.

Однако в последнее время разработчики по ряду причин стремятся отказаться от использования файлов cookie и сеансов сервера и ищут альтернативные методы аутентификации.

Одним из них является использование JSON Web Token (JWT) — токена, содержащего в зашифрованном виде всю минимально необходимую информацию для аутентификации и авторизации.

В этом случае нет необходимости хранить пользовательские данные в сессии, поскольку токен автономен.

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

Основные причины, по которым разработчики могут отказаться от файлов cookie и сеансов, на мой взгляд, следующие:

  • Разработчики все чаще переходят на одностраничные веб-приложения (SPA) и получают доступ к своему серверу через API. Они используют один и тот же API для обслуживания мобильных приложений.

    А чтобы унифицировать подход к аутентификации, предпочитают использовать токены доступа, поскольку использование файлов cookie на мобильных платформах затруднительно.

  • При горизонтальном масштабировании веб-приложения (веб-фермы) возникает проблема синхронизации состояния сеансов между серверами.

    Конечно, для этого есть решения, но проще создавать приложения без сохранения состояния, вообще не требующие использования сеанса.

    JWT решает эту проблему.

Сам JWT, как и файл cookie, имеет свой срок действия и в простейшем случае используется следующим образом:
  1. Пользователь запрашивает доступ с вашего сервера (и вообще с Сервера авторизации), отправив ему логин и пароль.

  2. Сервер авторизации проверяет валидность пользователя и отправляет ему токен доступа, который имеет определенный срок действия (например, через 2 недели).

  3. Пользователь использует этот токен доступа для доступа к ресурсам на вашем сервере (и в целом на Ресурсном сервере).

  4. По истечении срока действия (через 2 недели) пользователю придется пройти процедуру аутентификации повторно.

Основным недостатком этого подхода является то, что в случае короткого срока действия пользователю придется часто вводить имя пользователя и пароль (что неудобно и менее безопасно с точки зрения частой передачи пароля).

Как вариант предлагается просто использовать длительный срок действия (например, 1 год).

Однако такой подход порождает ряд проблем:

  • Если токен доступа будет украден (например, через XSS-уязвимость), злоумышленник сможет получить доступ к ресурсу на длительный период.
  • Если администратор захочет ограничить права пользователя или изменить его роль, пользователю придется пройти процедуру аутентификации заново, чтобы токен доступа обновился.

Для решения описанных проблем часто предлагается наряду с токеном кратковременного доступа дополнительно использовать второй токен долговременного обновления.

В этом случае при аутентификации пользователь получает токен обновления (со сроком действия, например, 1 год) и токен доступа (например, со сроком действия 30 минут).

А для доступа к ресурсам он по-прежнему использует токен доступа, но теперь через 30 минут, чтобы получить новый токен доступа, ему достаточно отправить свой токен обновления на Сервер Авторизации и он в ответ отправит ему свежий токен доступа.

, проверив еще раз Права пользователя.

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

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

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

В случае кражи токена такой подход приводит к тому, что злоумышленник может бесконечно использовать ресурс.

Во втором случае делается то же самое, но только для токена обновления.

Вот все подходы, которые мне удалось найти.

Я в свою очередь хотел бы для простоты ограничиться одним токеном доступа, но при этом иметь скользящий срок действия и возможность менять права и ограничивать доступ к токену в случае его кражи.

Для этого я бы добавил в токен новое поле RefreshDate (дата, после которой токен необходимо обновить; должна быть меньше даты окончания срока действия, если она указана) и только одно поле в пользовательской таблице в базе данных — MinRefreshDate .

В этом поле должно храниться минимальное значение RefreshDate, допустимое для пользователя.

В этом случае для обновления токена значение MinRefreshDate должно быть непустым и всегда должно быть меньше, чем RefreshDate самого токена, который необходимо обновить.

Процесс использования будет выглядеть примерно так:

  1. Допустим, сегодня 1 января 1789 года.

    Период обновления составит 3 дня.

    MinRefreshDate для пользователя не указан (NULL).

  2. Пользователь первый раз отправляет логин/пароль на Сервер авторизации и получает в ответ токен доступа с RefreshDate = 04.01.89. В этом случае сервер видит, что MinRefreshDate пуст, и делает его равным 04.01.89.
  3. Пользователь использует токен доступа 1, 2 и 3 января для доступа к серверу ресурсов.

  4. Администратор меняет роль пользователя 2 января.

  5. При следующем запросе пользователя 4 января (или позже) Ресурсный Сервер понимает, что токен доступа необходимо обновить, и сам запрашивает его у Сервера Авторизации.

  6. Сервер авторизации проверяет, что MinRefreshDate не пуст и меньше RefreshDate из токена, а также проверяет текущие права пользователя и генерирует новый токен доступа с RefreshDate = 07.01.89 и новой ролью пользователя.

  7. Сервер ресурсов отправляет пользователю новый токен доступа вместе с ресурсами.

  8. Пользователь продолжит использовать новый токен доступа 4 и 5 января под новой ролью.

  9. 6-го числа токен доступа был украден злоумышленником.

    Однако пользователь это замечает (например, если он получил уведомление о том, что к его профилю не обращались с обычного IP или браузера)

  10. В тот же день пользователь заходит в настройки своего профиля и нажимает что-то вроде «закрыть все сеансы и выйти из системы».

    Это сбрасывает MinRefreshDate для этого пользователя.

  11. 7-го числа злоумышленник пытается обновить украденный токен, но не может, поскольку MinRefreshDate = NULL.
  12. 8-го числа пользователь повторно выполняет процедуру аутентификации и отправляет свой логин/пароль.

    В этом случае он получает новый токен с RefreshDate = 11.01.89. В этом случае сервер видит, что MinRefreshDate пусто и делает его также равным 11.01.89 (в случае уже заполненной даты он этого не делает)

  13. 9-го числа злоумышленник снова пытается обновить токен (у которого RefreshDate = 07.01.89), но не может, поскольку его RefreshDate меньше MinRefreshDate.
Вот и все решение.

По-прежнему существуют проблемы, связанные с временным окном перед RefreshDate роли токена, который украден или требует обновления.

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

Но все эти проблемы частично можно решить, уменьшив продолжительность периода обновления (например, до 30 минут) и усилив контроль за необычной активностью пользователей.

У меня нет иллюзий, что этот подход будет кому-то интересен и будет использоваться на практике, но хотелось бы услышать мнение о том, насколько все это безопасно и пригодно для реального использования.

P.S: Конечно, в реальном проекте дополнительная безопасность должна быть обеспечена с помощью SSL и токена синхронизации (Anti-Forgery Token).

Кроме того, вместо MinRefreshDate вы можете использовать некоторую уникальную последовательность символов (например, SessionToken).

Но в этом случае JWT также придется дополнительно добавить поле SessionToken, чтобы его можно было проверить.

Вы также можете хранить набор SessionTokens для каждого пользователя (который будет создаваться для каждой аутентификации), чтобы более гибко контролировать и ограничивать определенные токены.

Спасибо.

Теги: #jwt #oauth #api #bearer #скользящий срок действия #информационная безопасность #JavaScript #api

Вместе с данным постом часто просматривают: