Насколько безопасно хранить токен обновления в базе данных? (Для выпуска новых токенов доступа для входа в систему). Или есть способ сделать это попроще?

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

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

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

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

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

Для чего тогда нужен токен обновления?

Поскольку я следую некоторым руководствам, чтобы добиться этого, вот как выглядит мой маршрут refresh_token

 //get a new access token with a refresh token
app.post('/refresh_token', (req, res) => {
    const token = req.cookies.refreshtoken
    //if no token in request
    if(!token) return res.send({accesstoken : ''});
    //if we have a token we verify it
    let payload = null;
    try{
        payload = verify(token, process.env.REFRESH_TOKEN_SECRET);
    }catch(err){
        return res.send({accesstoken: ''});
    }
    //if token is valid check if user exist
    const user = fakeDB.find(user => user.id === payload.userId)
    if(!user) return res.send({ accesstoken: ''});
    //if user exists check if refreshtoken exist on user

    //Is this really necessary? <-------------------------------------------

     if(user.refreshtoken !== token){
         return res.send({accesstoken: ''}) 
     }


    //if token exist create a new Refresh and Accestoken
    const accesstoken = createAccessToken(user.id);
    const refreshtoken =  createRefreshToken(user.id);
    user.refreshtoken = refreshtoken;
    //send new refreshtoken and accesstoken
    sendRefreshToken(res, refreshtoken);
    return res.send({accesstoken});
})

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

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

Есть ли способ сделать это проще, чем с jwt?


person mouchin777    schedule 28.12.2019    source источник
comment
см. stackoverflow.com/a/39003201/4855817 и stackoverflow.com/a/57503520/5175709   -  person Alex    schedule 28.12.2019


Ответы (1)


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

Вот как я бы это сделал:

  • 5-минутный токен доступа как JWT (самодостаточный, его нигде хранить не нужно).

  • 7-дневный токен обновления для одноразового использования: сгенерируйте случайный секрет (не нужно его подписывать / зашифровать), сохраните его в Redis с 7-дневным TTL (или MySQL с меткой времени valid_until). /refresh_token проверьте предоставленный токен (проверьте, есть ли он в Redis / MySQL) и удалите его. Создайте новую пару токенов доступа и обновления. (Мне также нравится вращать токены обновления, это делает его немного более безопасным: он, вероятно, уже повернут = недействителен в случае кражи)

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

person h3yduck    schedule 28.12.2019