TL;DR
- 您必须验证服务器中的 JWS 签名始终。
-
客户端签名验证并没有提供太多帮助,除非您有特定情况认为它是有意义的不要这样做。
- 您无需验证 JWS 令牌的签名即可在客户端中检查过期。 (除非您使用 JWE 对声明进行加密,在这种情况下,您需要做类似的事情,因为您需要密钥来解密声明)。
- 您不需要验证 JWS 的签名来检查服务器中的过期时间,但您应该这样做,因为这样可以确定没有人更改过期时间(否则验证将失败,因为如果声明发生更改,那么重新计算的签名会有所不同)
- 要读取未加密的声明,您只需对其进行解码。您可以在客户端中使用jwt-decode。
我现在意识到,在令牌过期后,我的前端仍将允许用户请求我的 api 端点 [...]
所以要实现这个逻辑,我想我需要在客户端验证 JWT 令牌
如果我对您的理解正确,您说的是在客户端检查 JWS 是否已过期。
为此,您无需验证令牌签名(尽管您使用的库似乎正在为您执行both things at the same time,但也允许您使用ignoreExpiration 标志禁用过期控制)。 (除非您正在加密声明,也就是使用 JWE)
RFC 7515 (JWS) 没有说明过期。 Message Signature or MAC Validation 不控制过期(它不应该因为签名为您提供真实性和完整性)。
如果 JWT is valid or not,即使 RFC 7519 (JWT) 也无法控制到期声明以进行解析。
还有,所有的claims are optional。
因此,您可以在不验证签名的情况下检查 JWT 是否已过期,因此您不需要公钥(用于 RSA 等非对称加密)或密钥(用于 AES 等对称加密)。
在 JWT 和 JWS 令牌中,声明只是纯文本 base64 编码,因此您只需 decode the payload without verifying if the signature is valid 并阅读到期声明。
如果您正在加密有效负载(也就是使用 JWE),那么您将无法执行此操作。
来自jjwt library的备注
JWT 可以加密签名(使其成为JWS)或加密(使其成为JWE) .
Here 是一个来自 auth0 的 ligthweigth 库,用于解码 JWT/JWS 令牌的 base64 编码声明。
一个人甚至在询问checking expiration。
我不知道你为什么认为你应该在客户端做这个控制,唯一的好处是避免发送客户端知道会失败的 API 请求。他们应该会失败,因为服务器应该验证令牌没有过期,显然之前的签名验证(使用密钥/私钥)。
RFC 7519 谈到了这一说法:
“exp”(过期时间)声明标识过期时间
或之后,JWT 不得接受处理。
在您所说的 Web 应用程序中,令牌的使用是允许无状态服务器对客户端请求进行身份验证。
OPTIONAL 过期声明的目标是允许服务器对生成的 JWS 有一些控制(如果我们使用 JWT 进行身份验证,对它们进行签名是必须的,所以我们应该谈论 JWS)。
如果没有过期,令牌将永远有效,或者直到用于签名它们的密钥发生变化(这将使验证过程失败)。
顺便说一句,invalidatingsessions 是使用无状态身份验证最臭名昭著的缺点之一。
如果我们在用于授权的 JWS 有效负载(又名声明)中包含信息,例如用户拥有哪些角色,会话失效将成为一个真正的问题。
来自Stop using JWT for sessions
但更严重的是,这也可能意味着某人拥有具有管理员角色的令牌,即使您刚刚撤销了他们的管理员角色。因为您也不能使令牌无效,所以您无法删除其管理员访问权限
过期控制并没有解决这个问题,我认为更倾向于避免会话劫持或 CSRF 攻击。
使用 CSRF 的攻击者将能够向您的 API 发出带有过期 JWS 的请求,从而跳过过期控制。
另一个问题是使用公钥或私钥验证客户端中的签名。
关于你的问题
我使用的似乎需要一个公钥才能使用它的 verify() 函数。我好像没有公钥,只有一个秘密,是我自己编的,所以不是用pair生成的。
您指出的验证方法明确表示它接受公钥或密钥。
jwt.verify(token, secretOrPublicKey, [options, callback])
secretOrPublicKey 是一个字符串或缓冲区,其中包含 HMAC 算法的密钥或 RSA 和 ECDSA 的 PEM 编码公钥
我假设您两者都没有使用,而您使用的是像 'shhhh' 这样的字符串。
var token = jwt.sign({ data: '¿Donde esta Santiago?'}, 'shhhh');
那你应该这样做
var decoded = jwt.verify(token, 'shhhhh');
然而,这里的问题是:客户端签名验证真的需要吗?
我认为不是,至少对于这种客户端仅使用 JWS 向服务器发送后续请求的应用程序来说:“嘿服务器,我是 Gabriel,我在这里有一篇论文(令牌)向您保证,并且该文件已由您签名。”
因此,如果客户端不验证 JWS 并且 MITM 已成功向该客户端提供了一个由他自己签名的 JWS(而不是由服务器签名的 JWS),那么后续请求将简单地失败。
和过期控制一样,签名验证只是防止客户端发出会失败的请求。
现在,客户端验证需要发送公钥或私钥。
发送公钥并不代表安全问题,但它是额外的努力和处理,几乎没有什么好处。
发送密钥(如“shhhh”)可能表示安全问题,因为用于签署令牌的密钥相同。