在数据库中存储刷新令牌是否安全? (为登录目的颁发新的访问令牌)。还是有一种方法可以更轻松地做到这一点?

Is it secure to store a refresh token in the database? (To issue new access tokens for login purposes). Or is there a method to do it easier?

目前我正在尝试收集有关如何实施身份验证系统(登录)的知识。在我的研究过程中,我尝试在我的后端实现一个基于 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});
})

箭头注释是我怀疑的地方,如果我的数据库 table 用户(它是一个模拟数据库,所以到目前为止是一个数组)没有存储刷新令牌,那么它返回一个空的访问令牌。但你为什么要那样做?那是用来不让任意用户生成访问令牌的吗?据我了解,这是我这样做的唯一原因。

但是话又说回来,存储在数据库中不是很危险吗?为什么不简单地存储访问令牌然后使其成为长期令牌,并在每次登录时生成一个新令牌?

有没有比 jwt 更简单的方法?

为什么访问令牌应该是短暂的:如果你想要一个分散的身份验证流程(身份验证服务签署一个令牌,其他服务可以使用非对称 public 密钥验证它是否有效),你需要那个令牌是短暂的,因为它不能被列入黑名单以防它被盗(攻击者可以使用它直到它过期)。您当然可以使用 Redis 将访问令牌列入黑名单,但您的身份验证流程将不再分散。所有服务都必须使用非对称 public 密钥验证该令牌并检查它是否被列入黑名单(最好只询问身份验证服务是否有效)。

这就是我的处理方式:

  • 5分钟访问令牌作为JWT(自包含,不需要存储它 任何地方)。

  • 一次性使用的 7 天刷新令牌:生成随机秘密(不需要签署 it/encrypt),将其存储在 Redis 中,TTL 为 7 天(或 MySQL 带有 valid_until 时间戳)。在 /refresh_token 上验证提供的令牌(检查它是否在 Redis/MySQL 中)并删除它。生成新的访问和刷新令牌对。 (我也喜欢轮换刷新令牌,这使它更安全:如果被盗,它可能已经轮换=无效)

通过这种方式,授权流程保持去中心化,如果刷新令牌被盗,可以将其撤销。