如何在 JWT 中使用 jti 索赔

JWT 规格提到了一个据称可以用来防止重播攻击的 jti 声明:

“ jti”(JWT ID)声明为 JWT 提供了一个唯一标识符。标识符值的分配方式必须确保同一值意外分配给不同数据对象的概率可以忽略不计; 如果应用程序使用多个发行者,不同发行者产生的值之间也必须避免冲突。可以使用“ jti”声明来防止重播 JWT。“ jti”值是区分大小写的字符串。使用此索赔是可选的。

我的问题是,我该如何实现这一点?我是否需要存储以前使用的 jtis 并在每个请求中发出一个新的 JWT?如果是这样,这是否违背了 JWT 的宗旨?为什么使用 JWT 而不是仅仅在数据库中存储随机生成的会话 ID?

我的 REST API 有一个 Mongo 数据库,我不反对添加一个 Redis 实例。还有比 JWT 更好的身份验证选项吗?我主要只是不想在客户端存储密码,因为这样可以避免 HTTP 身份验证,但是,随着我越来越深入地研究 JWT,我开始觉得自定义令牌实现或不同的标准可能更适合我的需要。对于基于令牌的身份验证,是否有支持令牌撤销和旋转令牌的节点/快速包?

希望你能给点建议。

123122 次浏览

Indeed, storing all issued JWT IDs undermines the stateless nature of using JWTs. However, the purpose of JWT IDs is to be able to revoke previously-issued JWTs. This can most easily be achieved by blacklisting instead of whitelisting. If you've included the "exp" claim (you should), then you can eventually clean up blacklisted JWTs as they expire naturally. Of course you can implement other revocation options alongside (e.g. revoke all tokens of one client based on a combination of "iat" and "aud").

You can use express-jwt package

See express-jwt on GitHub or on NPM.

Express-jwt handles revoked tokens as described here: https://github.com/auth0/express-jwt#revoked-tokens

var jwt = require('express-jwt');
var data = require('./data');
var utilities = require('./utilities');


var isRevokedCallback = function(req, payload, done){
var issuer = payload.iss;
var tokenId = payload.jti;


data.getRevokedToken(issuer, tokenId, function(err, token){
if (err) { return done(err); }
return done(null, !!token);
});
};


app.get('/protected',
jwt({secret: shhhhhhared-secret,
isRevoked: isRevokedCallback}),
function(req, res) {
if (!req.user.admin) return res.send(401);
res.send(200);
});

You can also read part 4. How do we avoid adding overhead? from this oauth0 blog post.

This is an old question, but I just worked on something similar. So I will share my thoughts here.

First of all, I agree that making database calls while validating JWT tokens undermines their primary advantage of being stateless.

None of the previous answers mention refresh tokens, but I believe they present a good trade-off between scalability and security.

In short, one can use regular auth tokens with a short expiration time (say, 15 minutes) and refresh tokens with long-lived access (say, 2 weeks). Whenever an auth token expires, the refresh token (stored more securely) is used to generate a new auth token without the user having to log in again.

The jti claim is best suited for refresh tokens. That gives you the ability to revoke access while minimizing the number of database calls made.

Let's say an average user session is 30 minutes. If you have a jti claim on regular auth tokens, then every API call is doing at least one extra database call to check if the token is not blacklisted. However, if you're only using a jti claim on refresh tokens, you will only make 2 database calls for authentication purposes during the course of a 30 minutes session (assuming each auth token expires after 15 minutes). That's a big difference.

Regarding implementation, you can use a randomly-generated UID and use it as your table's primary key. That guarantees the calls are as fast as possible. Moreover, you can add an expiration_time column with the same value as the exp claim. That way you can easily (batch) remove all the expired refresh tokens.

Why use a JWT instead of just storing a randomly-generated session ID in a database?

Or, if I may paraphrase, "why would you use a JWT refresh token rather than a random string saved on the database?"

I think you can do that, but using a JWT token has at least two advantages: (1) if the token is invalid or expired (when you decode it), you don't have to make any database calls at all. You just return a response with an error status code. (2) if you have a big system, you might be storing the "random strings" in different database tables based on some criteria (say, per client application [web vs mobile]). How would you know the table in which to look up the random string? With a JWT token, you can simply add a client_id claim. So, the ability to have information in the token is useful.

While in most cases JWT tokens should be validated offline, there are cases in which it might make sense to validate online. Your application almost certainly exposes many types of actions, some of which are more risky (and probably less frequent) than others. These risky actions could sometimes benefit from online token validation, since by the time the token is validated, it might not be safe to use it anymore (as it might have been revoked, for instance, which is particularly visible for longer-lived JWTs - which in turn minimize the pressure on refreshes). Another thing is that you might have some sort of a risk engine solution, which can decide whether issuing a token have been a good decision or not.