我有一个带角度前端的nodejs api。 API成功地使用JWT和护照来保护它的端点。
我现在意识到,在令牌过期后,我的前端仍然允许用户请求我的api端点,而不会提示他们重新输入他们的登录详细信息以获得新的令牌。
这就是我的后端生成令牌的方式:
function generateToken(user) {
return jwt.sign(user, secret, {
expiresIn: 10080 // in seconds
});
}
因此,要实现此逻辑,我认为我需要验证JWT令牌客户端。 Q1,这是一种明智的做法。
Q2,我使用的JWT
库似乎需要公钥来使用它的verify()
功能。我似乎没有公钥,只有一个秘密,我刚刚组成,所以它不是用一对产生的。我的公钥来自哪里,或者有没有其他方法来验证我的令牌?
这一切似乎都应该是显而易见的,我已经错过了一些东西,如果这是一个愚蠢的问题,请道歉,但我似乎无法找到答案?
答案 0 :(得分:14)
<强> TL; DR 强>
我现在意识到,在令牌过期后,我的前端仍然允许用户请求我的api端点[...]
因此,要实现此逻辑,我认为我需要验证JWT令牌客户端
如果我理解正确,那么您正在讨论检查JWS是否已在客户端过期。
为了做到这一点,您不需要验证令牌签名(尽管您使用的库似乎正在为您both things at the same time执行,但也允许您使用ignoreExpiration
禁用到期控制旗)。 (除非您正在加密声明,即使用JWE)
RFC 7515 (JWS)没有说明过期。 Message Signature or MAC Validation无法控制过期(并且它不应该因为签名为您提供真实性和完整性)。
如果JWT RFC 7519 (JWT),即使is valid or not也无法控制到期声明以解决问题。
此外,所有claims are optional。
因此,您可以在不验证签名的情况下检查JWT是否已过期,因此您既不需要公钥(对于非对称加密,如RSA),也不需要密钥(对于AES等对称加密) 。 在JWT和JWS令牌中,声明只是明文base64编码,因此您只需decode the payload without verifying if the signature is valid并阅读到期声明。 如果您正在加密有效负载(也就是使用JWE),那么您将无法执行此操作。
的说明JWT可以加密签名(使其成为 JWS )或加密(使其成为 JWE )
Here是来自auth0的 ligthweigth库,用于解码JWT / JWS令牌的base64encoded声明。 一个人甚至询问checking expiration。
我不知道为什么你认为你应该在客户端做这个控制,唯一的好处是避免发送客户端知道会失败的API请求。它们应该失败,因为服务器应该验证令牌没有过期,以前的签名验证(带有密钥/私钥)。
RFC 7519说了这个说法:
&#34; exp&#34; (到期时间)声明标识到期时间 或者在此之后JWT 不得接受处理。
在类似的网络应用程序中,您说使用令牌是允许无状态服务器对客户端请求进行身份验证。 可选到期声明的目标是允许服务器对生成的JWS进行一些控制(如果我们使用JWT进行身份验证签名是必须的,那么我们应该讨论JWS)。
如果没有过期,令牌将永久有效或直到用于签名的密钥发生更改(这将使验证过程失败)。 顺便说一下,invalidating sessions是使用无状态身份验证的最臭名昭着的缺点之一。
如果我们在用于授权的JWS有效负载(也称为声明)中包含信息(例如用户具有哪些角色),则会话失效将成为一个真正的问题。
但更严重的是,它也可能意味着某人拥有一个具有管理员角色的令牌,即使您刚刚撤销了他们的管理员角色。由于您无法使令牌无效,因此无法删除其管理员权限
到期控制并没有解决这个问题,我认为更倾向于避免会话劫持或CSRF攻击。
使用CSRF的攻击者将能够通过过期的JWS向您的API发出请求,跳过过期控制。
另一个问题是使用公钥或密钥验证客户端中的签名。
关于你的问题
我正在使用似乎需要公钥才能使用它的verify()函数。我似乎没有公钥,只有一个秘密,我刚刚组成,所以它不是用一对产生的。
您指出的验证方法明确表示它接受公钥或密钥。
jwt.verify(token, secretOrPublicKey, [options, callback])
secretOrPublicKey是一个字符串或缓冲区,包含HMAC算法的秘密,或者用于RSA和ECDSA的PEM编码公钥
我假设您没有使用,而且您使用的字符串类似于&#39; shhhh&#39;。
var token = jwt.sign({ data: '¿Donde esta Santiago?'}, 'shhhh');
然后你应该做
var decoded = jwt.verify(token, 'shhhhh');
但是,这里的问题是:真的需要客户端签名验证吗?
我认为不是,至少不是这种应用程序,客户端只是使用JWS向服务器发送后续请求说:&#34;嘿服务器,我是加布里埃尔和我有一篇论文(令牌)这里确保那份文件由你签署。&#34; 因此,如果客户端没有验证JWS并且MITM已经成功地向该客户端提供了由他自己签名的JWS(而不是由服务器签名的JWS),那么后续请求将会失败。 与过期控制一样,签名验证仅阻止客户端发出失败的请求。
现在,客户端验证需要发送公钥或密钥。 发送公钥并不代表安全问题,但它的额外工作和处理几乎没有任何好处。
发送密钥(例如&#39; shhhh&#39;)可能代表安全问题,因为它与用于签署令牌的密钥相同。
答案 1 :(得分:1)
Q1:客户端上的令牌验证不是一个好主意。您可以做的是在客户端上将令牌与相同的过期日期一起保存,然后刷新/删除令牌。但我认为在服务器端进行日期检查更好会导致存在简单的规则:不信任客户端因为它总是可以发送恶意代码。
Q2: JWT不需要任何公钥。它总是必须在服务器端存储私钥,因为如果有人知道你的密钥你的令牌没有任何意义。您只能添加一些有效负载来使其更复杂。
答案 2 :(得分:0)
我认为在客户端验证JWT令牌并不是一个好主意 IMO;
每当用户登录时,生成访问权限并刷新令牌并返回给用户这样的内容;
{
"accessToken": <<accessToken>>
"refreshToken": <<refreshToken>>
"expiresAt": <<expiresAt>>
}
因此,客户端可以了解访问令牌何时到期,并可以使用刷新令牌刷新它。
加密您在访问令牌中放置的数据,因为有机会在没有密钥的情况下访问数据。但当然有人需要秘密密钥来验证。
答案 3 :(得分:0)
把它放在这里给那些来这里寻找使用公钥在浏览器上进行 jwt 验证的人。
图书馆: https://kjur.github.io/jsrsasign/
示例: https://kjur.github.io/jsrsasign/tool/tool_jwtveri.html
代码示例: https://github.com/kjur/jsrsasign/blob/master/tool/tool_jwtveri.html
API: https://kjur.github.io/jsrsasign/api/symbols/KJUR.jws.JWS.html#.verifyJWT
附言不要使用您的密钥进行浏览器 jwt 验证!仅限公钥!
答案 4 :(得分:-2)
答案1 :在客户端验证您的身份验证令牌并不是一个好方法,因为它在编码/解码时涉及密钥并且保持密钥在客户端不安全。
创建令牌
jwt.sign({
data: 'foobar'
}, 'secret', { expiresIn: 60 * 60 });
验证令牌
jwt.verify(token, 'secret', function(err, decoded) {
console.log(decoded.foo) // bar
});
答案2 :JWT在编码和解码令牌时涉及密钥 OR 公钥。它必须在服务器端的某个地方声明或保存在配置文件中。
说明: 解码意味着从Base64解码,该进程中没有秘密密钥。另一方面,验证JWT需要密钥,因为它涉及加密签名操作。
总而言之,解码不需要秘密(记住解码只是解释base64)并且验证/签名确实需要它