Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/42.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 使JSON Web令牌无效_Javascript_Node.js_Session_Session Cookies_Jwt - Fatal编程技术网

Javascript 使JSON Web令牌无效

Javascript 使JSON Web令牌无效,javascript,node.js,session,session-cookies,jwt,Javascript,Node.js,Session,Session Cookies,Jwt,对于我正在处理的一个新node.js项目,我正在考虑从基于cookie的会话方法(我的意思是,将id存储到用户浏览器中包含用户会话的键值存储)切换到使用JSON Web令牌(jwt)的基于令牌的会话方法(无键值存储) 该项目是一个利用socket.io的游戏-在一个会话中有多个通信通道(web和socket.io)的情况下,使用基于令牌的会话将非常有用 如何使用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});
    });
}
基于令牌的登录:

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 具有指定令牌的数据库


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

我也一直在研究这个问题,虽然下面的想法都不是完整的解决方案,但它们可能会帮助其他人排除想法,或提供进一步的想法

1) 只需从客户端删除令牌

显然,这对服务器端安全没有任何作用,但它确实通过删除令牌来阻止攻击者(即,他们必须在注销之前窃取令牌)

2) 创建令牌阻止列表

您可以将无效令牌存储到其初始到期日,并将其与传入请求进行比较。这似乎否定了首先完全基于令牌的原因,因为您需要为每个请求触摸数据库。但是存储大小可能会更低,因为您只需要存储在注销和到期时间之间的令牌(这是一种直觉,并且肯定取决于上下文)

3) 只需缩短令牌到期时间并经常轮换即可

如果您将令牌到期时间保持在足够短的时间间隔内,并让正在运行的客户端跟踪并在必要时请求更新,那么1号将作为一个完整的注销系统有效地工作。这种方法的问题在于,它使用户无法在客户端代码关闭之间保持登录(取决于您的到期时间间隔)

应急计划

如果发生紧急情况,或者用户令牌被破坏,您可以做的一件事是允许用户使用其登录凭据更改基础用户查找ID。这将导致所有关联的令牌无效,因为将无法再找到关联的用户

我还想指出,最好将最后一次登录日期包含在令牌中,这样您就可以在一段较长的时间后强制重新登录


关于使用令牌的攻击的相似性/差异,这篇文章提出了一个问题:

我会在用户模型上记录jwt版本号。新的jwt令牌将其版本设置为此

验证jwt时,只需检查它的版本号是否与用户当前的jwt版本相同


任何时候你想使旧的jwt失效,只需点击用户的jwt版本号。

上面的想法很好,但是一个使所有现有jwt失效的非常简单的方法就是简单地更改秘密

如果您的服务器创建了JWT,用一个秘密(JWS)对其进行签名,然后将其发送到客户端,只需更改该秘密即可使所有现有令牌无效,并要求所有用户获得一个新令牌以进行身份验证,因为根据服务器,他们的旧令牌突然变得无效

它不需要对实际令牌内容(或查找ID)进行任何修改


显然,这仅适用于您希望所有现有令牌过期的紧急情况,对于每个令牌过期,需要上述解决方案之一(例如缩短令牌过期时间或使令牌内存储的密钥无效)。

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

我看到的主要缺点是,如果他们在多个浏览器中,或者也有一个移动客户端,它会将他们从所有会话中注销


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

我来晚了一点,但我想我有一个不错的解决方案


我的数据库中有一个“last_password_change”列,它存储上次更改密码的日期和时间。我还将发布日期/时间存储在JWT中。在验证令牌时,我会检查令牌发出后密码是否已更改,以及是否已更改,即使令牌尚未过期,也会被拒绝。

为什么不使用jti声明(nonce)并将其作为用户记录字段存储在列表中(取决于数据库,但至少可以使用逗号分隔的列表)?不需要单独查找,正如其他人指出的那样,您可能无论如何都想获取用户记录,通过这种方式,您可以为不同的客户端实例拥有多个有效令牌(“到处注销”可以将列表重置为空)

还没有尝试过这种方法,而且它使用了大量信息
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);
user_id   revoke_tokens_issued_before
-------------------------------------
123       2018-07-02T15:55:33
567       2018-07-01T12:34:21
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
    });
jwtr.verify(token, secret);
jwtr.destroy(token)
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);
    });
}