对于我正在从事的一个新的node.js项目,我正在考虑从基于cookie的会话方法(我的意思是,将id存储到用户浏览器中包含用户会话的键值存储中)切换到使用JSON Web Tokens (jwt)的基于令牌的会话方法(没有键值存储)。

这个项目是一个利用socket的游戏。IO——在一个会话(web和socket.io)中有多个通信通道的情况下,有一个基于令牌的会话会很有用。

如何使用jwt方法从服务器提供令牌/会话失效?

我还想了解使用这种范例应该注意哪些常见的(或不常见的)陷阱/攻击。例如,如果这种模式容易受到与基于会话存储/cookie的方法相同/不同类型的攻击。

所以,假设我有以下内容(改编自this和this):

会话存储登录:

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 使用指定的令牌创建数据库。

在基于令牌的方法中似乎不存在这样的机制,因为令牌本身将包含通常存在于键值存储中的信息。


当前回答

如果“注销所有设备”选项是可接受的(在大多数情况下是):

将令牌版本字段添加到用户记录中。 将此字段中的值添加到存储在JWT中的声明中。 每次用户注销时增加版本。 在验证令牌时,将其版本声明与存储在用户记录中的版本进行比较,如果不相同则拒绝。

无论如何,在大多数情况下都需要db访问以获取用户记录,因此这不会给验证过程增加太多开销。与维护黑名单不同,在黑名单中,由于需要使用连接或单独调用、清除旧记录等,数据库负载非常重要。

其他回答

我是这样做的:

生成一个唯一的散列,然后将其存储在redis和JWT中。这可以称为会话 我们还将存储特定JWT发出的请求数量——每次JWT被发送到服务器时,我们将请求增加整数。(这是可选的)

因此,当用户登录时,将创建一个唯一的散列,存储在redis中并注入到JWT中。

当用户试图访问受保护的端点时,您将从JWT中获取唯一的会话散列,查询redis并查看它是否匹配!

我们可以以此为基础,让我们的JWT更加安全,如下所示:

每个特定JWT发出的X请求,我们生成一个新的唯一会话,将其存储在我们的JWT中,然后将前一个会话列入黑名单。

这意味着JWT是不断变化的,并防止过时的JWT被黑客攻击、窃取或其他东西。

在本例中,我假设最终用户也有一个帐户。如果不是这样,那么其他的方法也不太可能奏效。

创建JWT时,将其持久化到数据库中,并与正在登录的帐户相关联。这意味着您可以从JWT中提取关于用户的其他信息,因此根据环境的不同,这可能是可行的,也可能是不可行的。

对于之后的每个请求,不仅要执行您所使用的框架附带的标准验证(我希望)(验证JWT是否有效),还包括用户ID或另一个令牌(需要与数据库中的令牌匹配)之类的东西。

注销时,删除cookie(如果使用),并从数据库中使JWT(字符串)无效。如果不能从客户端删除cookie,那么至少注销过程将确保令牌被销毁。

我发现这种方法,加上另一个唯一标识符(数据库中有2个持久化项,可用于前端),会话具有很强的弹性

我一直在考虑的一种方法是在JWT中始终有一个iat(发布时间)值。然后,当用户注销时,将时间戳存储在用户记录上。在验证JWT时,只需将iat与上次注销的时间戳进行比较。如果iat更老,那么它是无效的。是的,您必须访问DB,但如果JWT在其他方面有效,我将始终提取用户记录。

我所看到的主要缺点是,如果他们在多个浏览器中,或者有一个移动客户端,它会让他们退出所有的会话。

这也是一种很好的机制,可以使系统中的所有jwt无效。部分检查可以针对最后有效的iat时间的全局时间戳。

代币的有效期为1天 每天建立一个黑名单。 将无效/注销令牌放入黑名单

对于令牌验证,首先检查令牌的过期时间,如果令牌没有过期,则检查黑名单。

对于长会话需求,应该有一种机制来延长令牌过期时间。

您可以在用户的文档/记录上的DB上有一个“last_key_used”字段。

当用户使用user和pass登录时,生成一个新的随机字符串,将其存储在last_key_used字段中,并在签名令牌时将其添加到负载中。

当用户使用令牌登录时,检查数据库中的last_key_used是否与令牌中的last_key_used匹配。

然后,当用户执行注销操作时,或者如果您想使令牌失效,只需将“last_key_used”字段更改为另一个随机值,随后的任何检查都将失败,从而迫使用户使用user登录并再次通过。