Аннулирование веб-токенов JSON

Для нового проекта node.js, над которым я работаю, я думаю о переключении с подхода, основанного на использовании файлов cookie (я имею в виду сохранение идентификатора в хранилище значений ключей, содержащем сеансы пользователя в браузере пользователя). к подходу сеанса на основе токенов (без хранения значения ключа) с использованием веб-токенов JSON (jwt).

Проект представляет собой игру, в которой используется socket.io - полезно иметь сеанс на основе токенов в таком сценарии, когда в одном сеансе будет несколько каналов связи (web и socket.io).

Как обеспечить токен / недействительность сеанса с сервера, используя подход jwt?

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

Итак, скажем, у меня есть следующее (адаптировано из этого и этого):

Вход в магазин сессий:

app.get('/login', function(request, response) {
    var user = {username: request.body.username, password: request.body.password };
    // Validate somehow
    validate(user, function(isValid, profile) {
        // Create session token
        var token= createSessionToken();

        // Add to a key-value database
        KeyValueStore.add({token: {userid: profile.id, expiresInMinutes: 60}});

        // The client should save this session token in a cookie
        response.json({sessionToken: token});
    });
}

Логин на основе токена:

var jwt = require('jsonwebtoken');
app.get('/login', function(request, response) {
    var user = {username: request.body.username, password: request.body.password };
    // Validate somehow
    validate(user, function(isValid, profile) {
        var token = jwt.sign(profile, 'My Super Secret', {expiresInMinutes: 60});
        response.json({token: token});
    });
}

-

Выход (или аннулирование) для подхода хранилища сеансов потребует обновления базы данных KeyValueStore с указанным токеном.

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

35 ответов

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

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

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

Звучит ужасно сложно? это делает для меня!

Отказ от ответственности: я не использовал Redis.

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

Я думаю, что это не лучшее решение, но это работает для меня.

Если вам нужен быстрый, эффективный и элегантный logout функциональность для входа на основе JWT (что фактически является основной причиной аннулирования JWT) вот как: заменить текущий токен токеном, срок действия которого истекает, скажем, через 1 секунду.

Подробно:

  1. Когда вы делаете запрос от клиента с действующим присоединенным JWT, удалите JWT, который вы храните в своем локальном хранилище (если вы сохраняете его для своей функции «запомнить меня»).

  2. Затем, когда запрос поступает на сервер /logout обработчик маршрута:

    • Если входящий JWT действителен, создайте новый JWT, срок действия которого истекает через 1 секунду, и отправьте его обратно клиенту.
    • Вы можете сделать это с помощью redirectответ и токен в теле ответа. Перенаправление может быть на любой публичный маршрут (скажем. /home). Или вы можете ответить обычным ответом без перенаправления, чтобы позже выполнить желаемое перенаправление на клиенте.
  3. Сейчас на клиенте

    • Когда вы получите ответ, сохраните новый JWT в локальном хранилище, где вы только что удалили старый на шаге 1 в. Теперь при желании вы можете перенаправить на клиенте, чтобы не использовать ответ перенаправления с сервера.
    • Поскольку срок действия нового токена уже истек (истечение срока было установлено через 1 секунду), любая попытка перенаправления на защищенные маршруты (с уже истекшим токеном) должна перенаправлять на страницу регистрации / входа. Вы должны сами обеспечить это поведение на маршрутах, защищенных аутентификацией сервера.

Так просто. У вас только что появилась обычная функция выхода из системы по требованию с JWT.

Да, это не делает исходный JWT недействительным. Если кто-то (злоумышленник) захватил и сохранил его до того, как он сможет использовать его до истечения срока его действия. Однако его окно возможностей сужается со временем.

Но вот вторая часть решения: очень недолговечный (10 минут?) Оригинальный JWT и более долгоживущий токен обновления, сохраненный в БД и фактически отозванный или удаленный из БД.

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

Быстрое решение может применяться для требований безопасности проигравшего. Часть токенов обновления / черный список / белый список может быть добавлена ​​для более строгих требований безопасности.

В любом случае это более простой и расширяемый по запросу подход.

Что если вы просто сгенерируете новый токен с сервера с expiresIn: 0, вернете его клиенту и сохраните в cookie?

Если вы используете axios или аналогичную библиотеку HTTP-запросов на основе обещаний, вы можете просто уничтожить токен во внешнем интерфейсе внутри .then()часть. Он будет запущен в части ответа.then() после того, как пользователь выполнит эту функцию (код результата с конечной точки сервера должен быть в порядке, 200). После того, как пользователь щелкнет этот маршрут при поиске данных, если поле базы данныхuser_enabledложно, это вызовет уничтожение токена, и пользователь немедленно выйдет из системы и не сможет получить доступ к защищенным маршрутам / страницам. Нам не нужно ждать истечения срока действия токена, пока пользователь постоянно находится в системе.

function searchForData() {   // front-end js function, user searches for the data
    // protected route, token that is sent along http request for verification
    var validToken = 'Bearer ' + whereYouStoredToken; // token stored in the browser 

    // route will trigger destroying token when user clicks and executes this func
    axios.post('/my-data', {headers: {'Authorization': validToken}})
     .then((response) => {
   // If Admin set user_enabled in the db as false, we destroy token in the browser localStorage
       if (response.data.user_enabled === false) {  // user_enabled is field in the db
           window.localStorage.clear();  // we destroy token and other credentials
       }  
    });
     .catch((e) => {
       console.log(e);
    });
}
Другие вопросы по тегам