fun*_*iki 357 javascript session session-cookies node.js jwt
对于我正在研究的新node.js项目,我正在考虑从基于cookie的会话方法切换(我的意思是,将id存储到包含用户浏览器中的用户会话的键值存储)使用JSON Web令牌(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});
});
}
Run Code Online (Sandbox Code Playgroud)
基于令牌的登录:
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});
});
}
Run Code Online (Sandbox Code Playgroud)
-
会话存储方法的注销(或无效)需要使用指定的令牌更新KeyValueStore数据库.
似乎这种机制在基于令牌的方法中不存在,因为令牌本身将包含通常存在于键值存储中的信息.
Mat*_*Way 315
我也一直在研究这个问题,虽然下面没有一个想法是完整的解决方案,但它们可能会帮助其他人排除想法或提供更多想法.
1)只需从客户端删除令牌
显然,这对服务器端安全性没有任何作用,但它确实通过删除令牌来阻止攻击者(即,他们必须在注销之前窃取令牌).
2)创建令牌黑名单
您可以将无效令牌存储到其初始到期日期,并将它们与传入请求进行比较.这似乎否定了首先完全基于令牌的原因,因为您需要为每个请求触摸数据库.存储大小可能会更低,因为您只需要存储在注销和到期时间之间的令牌(这是一种直觉,并且肯定取决于上下文).
3)只需保持令牌到期时间短并经常旋转它们
如果您以足够短的间隔保持令牌到期时间,并让正在运行的客户端跟踪并在必要时请求更新,则数字1将有效地用作完整的注销系统.这种方法的问题在于,它使得用户无法在关闭客户端代码之间保持登录(取决于您到达时间间隔的时间长度).
临时计划
如果发生紧急情况或用户令牌遭到入侵,您可以做的一件事是允许用户使用其登录凭据更改基础用户查找ID.这将使所有关联的令牌无效,因为将无法再找到关联的用户.
我还想注意,最后一个登录日期包含令牌是一个好主意,这样您就可以在一段遥远的时间后强制执行重新登录.
关于使用令牌的攻击的相似性/差异,本文解决了以下问题:http://blog.auth0.com/2014/01/07/angularjs-authentication-with-cookies-vs-token/
小智 76
上面发布的想法很好,但是让所有现有JWT无效的一种非常简单易行的方法就是改变秘密.
如果您的服务器创建了JWT,请使用机密(JWS)对其进行签名,然后将其发送给客户端,只需更改密钥即可使所有现有令牌无效,并要求所有用户获取新令牌以进行身份验证,因为旧令牌突然变为无效到服务器.
它不需要对实际令牌内容(或查找ID)进行任何修改.
显然,这仅适用于您希望所有现有令牌到期的紧急情况,对于每个令牌到期,需要上述解决方案之一(例如短令牌到期时间或使令牌内存储的密钥无效).
Ed *_*d J 59
这主要是一篇长篇评论,支持并建立在@mattway的答案之上
鉴于:
此页面上的其他一些建议的解决方案主张在每个请求上访问数据存储区.如果您点击主数据存储区以验证每个身份验证请求,那么我认为使用JWT而不是其他已建立的令牌身份验证机制的理由更少.如果你每次都去数据存储区,你基本上使JWT成为有状态,而不是无状态.
(如果您的站点收到大量未经授权的请求,那么JWT会拒绝它们而不会访问数据存储区,这很有帮助.可能还有其他用例.)
鉴于:
对于典型的真实世界Web应用程序,无法实现真正的无状态JWT身份验证,因为无状态JWT无法为以下重要用例提供即时和安全支持:
用户的帐户被删除/阻止/暂停.
用户密码已更改.
用户的角色或权限已更改.
用户已由admin注销.
站点管理员更改JWT令牌中的任何其他应用程序关键数据.
在这些情况下,您不能等待令牌过期.令牌失效必须立即发生.此外,您不能相信客户端不会保留和使用旧令牌的副本,无论是否具有恶意.
因此:我认为来自@ matt-way,#2 TokenBlackList的答案是将所需状态添加到基于JWT的身份验证的最有效方式.
你有一个黑名单,持有这些令牌,直到它们的到期日期被击中.与用户总数相比,令牌列表将非常小,因为它只需要保留列入黑名单的令牌,直到它们到期为止.我通过在redis,memcached或其他支持在密钥上设置过期时间的内存数据存储中放置无效标记来实现.
您仍然必须为通过初始JWT身份验证的每个身份验证请求调用内存数据库,但您不必为其中的整个用户组存储密钥.(对于给定的站点,这可能是也可能不是什么大问题.)
Daf*_*onk 38
我会在用户模型上记录jwt版本号.新的jwt令牌会将其版本设置为此.
验证jwt时,只需检查它的版本号是否等于用户当前的jwt版本.
任何时候你想要使旧jwts无效,只需碰撞用户jwt版本号.
Ash*_*ian 31
还没有尝试过,它基于其他一些答案使用了很多信息.这里的复杂性是避免每次请求用户信息的服务器端数据存储调用.大多数其他解决方案需要对用户会话存储的每个请求进行数据库查找.这在某些情况下很好,但这是为了避免此类调用而创建的,并使所需的服务器端状态变得非常小.您将最终重新创建服务器端会话,无论多小都可以提供所有强制失效功能.但如果你想这样做,那就是要点:
目标:
解决方案:
这要求您在服务器上维护黑名单(状态),假设用户表包含禁止的用户信息.无效会话黑名单 - 是用户ID列表.仅在刷新令牌请求期间检查此黑名单.只要刷新令牌TTL,就必须存在条目.刷新令牌到期后,用户将需要重新登录.
缺点:
优点:
使用此解决方案,不需要像reddis这样的内存数据存储,至少不需要用户信息,因为服务器每隔15分钟左右只进行一次数据库调用.如果使用reddis,那么在那里存储有效/无效的会话列表将是一个非常快速和简单的解决方案.无需刷新令牌.每个身份验证令牌都有一个会话ID和设备ID,它们可以在创建时存储在reddis表中,并在适当时使其无效.然后,他们将在每个请求上进行检查,并在无效时被拒绝.
小智 13
我一直在考虑的方法是iat在JWT中始终具有(发布于)值.然后,当用户注销时,将该时间戳存储在用户记录中.验证JWT时,只需比较iat上次登出的时间戳即可.如果iat年龄较大,那么它无效.是的,您必须转到数据库,但如果JWT无效,我将始终提取用户记录.
我看到的主要缺点是,如果它们在多个浏览器中,或者也有移动客户端,它会将它们记录在所有会话中.
这也可以是使系统中所有JWT无效的良好机制.部分检查可能是针对上一个有效iat时间的全局时间戳.
Ama*_*pta 11
------------------------这个答案有点晚了,但可能会对某人有所帮助------ -----------
从客户端,最简单的方法是从浏览器的存储中删除令牌。
但是,如果你想销毁节点服务器上的令牌怎么办 -
JWT 包的问题在于它不提供任何方法或方式来销毁令牌。您可以对上面提到的 JWT 使用不同的方法。但在这里我使用 jwt-redis。
因此,为了销毁服务器端的令牌,您可以使用jwt-redis 包而不是 JWT
这个库 (jwt-redis) 完全重复了库 jsonwebtoken 的全部功能,并添加了一个重要的补充。Jwt-redis 允许您将令牌标签存储在 redis 中以验证有效性。redis 中缺少令牌标签使令牌无效。要销毁jwt-redis中的token,有一个destroy方法
它以这种方式工作:
1)从 npm 安装 jwt-redis
2)创建 -
var redis = require('redis');
var JWTR = require('jwt-redis').default;
var redisClient = redis.createClient();
var jwtr = new JWTR(redisClient);
jwtr.sign(payload, secret)
.then((token)=>{
// your code
})
.catch((error)=>{
// error handling
});
Run Code Online (Sandbox Code Playgroud)
3)验证——
jwtr.verify(token, secret);
Run Code Online (Sandbox Code Playgroud)
4)摧毁-
jwtr.destroy(token)
Run Code Online (Sandbox Code Playgroud)
注意:您可以在登录令牌期间提供 expiresIn 与 JWT 中提供的相同。
可能这对某人有帮助
小智 8
我在这里有点晚了,但我想我有一个不错的解决方案.
我的数据库中有一个"last_password_change"列,用于存储上次更改密码的日期和时间.我还将发行日期/时间存储在JWT中.在验证令牌时,我会检查在发出令牌后密码是否已更改,以及令牌是否被拒绝,即使它尚未过期.
HEADER:ALGORITHM & TOKEN TYPE
{
"alg": "HS256",
"typ": "JWT"
}
PAYLOAD:DATA
{
"sub": "1234567890",
"some": "data",
"iat": 1516239022
}
VERIFY SIGNATURE
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
HMACSHA256('perUserString'+'globalString')
)
where HMACSHA256 is your local crypto sha256
nodejs
import sha256 from 'crypto-js/sha256';
sha256(message);
Run Code Online (Sandbox Code Playgroud)
例如用法参见https://jwt.io(不确定他们处理动态 256 位机密)
我按照以下方式做到了:
unique hash,然后将其存储在redis和您的JWT中。这可以称为会话
因此,当用户登录时,会创建一个唯一的哈希值,将其存储在 redis 中并注入到您的JWT中。
当用户尝试访问受保护的端点时,您将从 JWT 获取唯一的会话哈希,查询 redis 并查看它是否匹配!
我们可以以此为基础进行扩展,使我们的JWT更加安全,具体方法如下:
特定JWT发出的每个X请求,我们都会生成一个新的唯一会话,将其存储在JWT中,然后将前一个会话列入黑名单。
这意味着JWT会不断变化,并阻止陈旧的JWT被黑客攻击、被盗或发生其他情况。
保持这样的内存列表
user_id revoke_tokens_issued_before
-------------------------------------
123 2018-07-02T15:55:33
567 2018-07-01T12:34:21
Run Code Online (Sandbox Code Playgroud)
如果您的令牌在一周内到期,则清除或忽略早于该时间的记录。也只保留每个用户的最新记录。列表的大小取决于您保留令牌的时间以及用户撤销令牌的频率。仅在表更改时使用 db。当您的应用程序启动时,将表加载到内存中。
为什么不直接使用 jti 声明(nonce)并将其作为用户记录字段存储在列表中(依赖于数据库,但至少一个逗号分隔的列表是可以的)?无需单独查找,正如其他人指出的,您可能无论如何都想获取用户记录,这样您就可以为不同的客户端实例拥有多个有效令牌(“到处注销”可以将列表重置为空)
小智 5
您可以在用户的文档/记录上的数据库中包含"last_key_used"字段.
当用户使用user登录并传递时,生成新的随机字符串,将其存储在last_key_used字段中,并在签名时将其添加到有效负载中.
当用户使用令牌登录时,请检查DB中的last_key_used以匹配令牌中的last_key_used.
然后,当用户执行注销时,或者如果要使令牌无效时,只需将"last_key_used"字段更改为另一个随机值,任何后续检查都将失败,从而强制用户使用用户登录并再次传递.
晚会晚了,经过一些研究,我在下面给出了两分钱。在注销期间,请确保发生以下事情...
清除客户端存储/会话
分别在登录或注销时更新用户表上次登录日期时间和注销日期时间。所以登录日期时间总是应该大于注销(或者如果当前状态是登录并且尚未注销,则保持注销日期为空)
这比保留额外的黑名单表和定期清除要简单得多。多设备支持需要额外的表格来保持登录、注销日期以及一些额外的细节,比如操作系统或客户端的细节。
| 归档时间: |
|
| 查看次数: |
168665 次 |
| 最近记录: |