JWT 验证客户端? [英] JWT Verify client-side?

查看:24
本文介绍了JWT 验证客户端?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有角度前端的 nodejs api.API 成功地使用带有护照的 JWT 来保护它的端点.

我现在意识到,在令牌过期后,我的前端仍将允许用户请求我的 api 端点,而不会提示他们重新输入登录详细信息以获取新令牌.

这是我的后端生成令牌的方式:

函数 generateToken(user) {返回 jwt.sign(用户,秘密,{expiresIn: 10080//以秒为单位});}

所以要实现这个逻辑,我想我需要在客户端验证 JWT 令牌.Q1,这是一个明智的做法.

Q2,我正在使用的 JWT 库似乎需要使用它的 verify() 函数的公钥.我似乎没有公钥,只有一个秘密,我只是编造的,所以它不是用一对生成的.我的公钥是从哪里来的,或者没有这个可以验证我的令牌的另一种方法?

这一切似乎应该是显而易见的,我错过了一些东西,如果这是一个愚蠢的问题,请道歉,但我似乎找不到答案?

解决方案

TL;DR

  1. 您必须始终验证服务器中的 JWS 签名.
  2. 客户端签名验证并没有提供太多帮助,除非您有特定的情况认为它是有意义的不要这样做.
  3. 无需验证 JWS 令牌的签名即可在客户端中检查过期.(除非您使用 JWE 对声明进行加密,在这种情况下,您需要做类似的事情,因为您需要密钥来解密声明).
  4. 您不需要验证 JWS 的签名来检查服务器中的过期时间,但您应该这样做,因为这可以让您确定没有人更改过期时间(否则验证将失败,因为如果声明更改然后重新计算的签名会有所不同)
  5. 要阅读未加密的声明,您只需对其进行解码.您可以在客户端中使用 jwt-decode.

<块引用>

我现在意识到,在令牌过期后,我的前端仍将允许用户请求我的 api 端点 [...]

<块引用>

所以要实现这个逻辑,我想我需要在客户端验证 JWT 令牌

如果我对您的理解正确,您说的是在客户端检查 JWS 是否已过期.为此,您不需要验证令牌签名(尽管您使用的库似乎正在执行 这两件事同时为您服务,还可以让您使用 ignoreExpiration 标志禁用过期控制).(除非您正在加密声明,也就是使用 JWE)RFC 7515 (JWS) 没有说明过期.消息签名或 MAC 验证 不控制过期(它应该'不是因为签名给你真实性和完整性).即使是 RFC 7519 (JWT) 也无法控制到期声明以解决问题,如果JWT 有效与否.

此外,所有声明都是可选的.

因此,您可以在不验证签名的情况下检查 JWT 是否已过期,因此您不需要公钥(用于 RSA 等非对称加密)或密钥(用于 AES 等对称加密).在 JWT 和 JWS 令牌中,声明只是纯文本 base64 编码,因此您可以解码有效载荷而不验证签名是否有效并读取过期声明.如果您正在加密有效负载(也就是使用 JWE),那么您将无法执行此操作.

来自 jjwt 库的注释

<块引用>

JWT 可以加密签名(使其成为JWS)或加密(使其成为JWE).

这里是一个来自 auth0 的 ligthweigth 库,用于解码 a 的 base64encoded 声明JWT/JWS 令牌.一个人甚至在问检查过期.

我不知道你为什么认为你应该在客户端做这个控制,唯一的好处是避免发送客户端知道会失败的 API 请求.他们应该会失败,因为服务器应该验证令牌没有过期,显然之前的签名验证(使用密钥/私钥).

RFC 7519 谈到了这一说法:

<块引用>

exp"(过期时间)声明标识过期时间或之后,JWT 不得接受处理.

在您所说的 Web 应用程序中,令牌的使用是允许无状态服务器对客户端请求进行身份验证.OPTIONAL 过期声明的目标是允许服务器对生成的 JWS 有一些控制(如果我们使用 JWT 进行身份验证,对它们进行签名是必须的,所以我们应该谈论 JWS).

如果没有过期,令牌将永远有效,或者直到用于签名它们的密钥发生变化(这将使验证过程失败).顺便说一句,无效 sessions 是最臭名昭著的会议之一使用无状态身份验证的缺点.

如果我们在用于授权的 JWS 有效负载(又名声明)中包含信息,例如用户拥有哪些角色,会话失效将成为一个真正的问题.

来自 停止使用 JWT会议

<块引用>

但更严重的是,这也可能意味着某人拥有具有管理员角色的令牌,即使您刚刚撤销了他们的管理员角色.因为您也不能使令牌无效,所以您无法删除其管理员访问权限

过期控制并没有解决这个问题,我认为更倾向于避免会话劫持或 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")可能表示安全问题,因为用于签署令牌的密钥相同.

I have a nodejs api with an angular frontend. The API is successfully using JWT with passport to secure it's endpoints.

I am now conscious that after the tokens have expired, my front end will still allow the user to request my api endpoints without prompting them to reenter their log in details to get a fresh token.

This is how my backend generates the token:

function generateToken(user) {
  return jwt.sign(user, secret, {
    expiresIn: 10080 // in seconds
  });
}

So to implement this logic I think I need to verify the JWT token client-side. Q1, is this a sensible approach.

Q2, the JWT library I am using seems to require a public key to use it's verify() function. I don't seem to have a public key, only a secret, which I just made up, so it wasn't generated with a pair. Where does my public key come from, or is there another way of verifying my token without this?

This all seems like it should be obvious and that I have missed something, so apologies if this is a stupid question, but I can't seem to find the answer?

解决方案

TL;DR

  1. You must verify the signature of JWS in the server always.
  2. Client-side signature verification doesn't gives much, unless you have a specific case where it makes sense don't do it.
  3. You don't need to verify the signature of a JWS token to check expiration in the client. (unless you were encrypting the claims, aka using JWE, in that case you need to do something similar because you need a key to decrypt the claims).
  4. You don't need to verify the signature of a JWS to check expiration in the server neither, but you should because this gives you the certainty that nobody has altered the expiration (otherwise the verification will fail because if the claims change then the recalculated signature will differ)
  5. To read non encrypted claims you just only need to decode them. You could use jwt-decode in the client.

I am now conscious that after the tokens have expired, my front end will still allow the user to request my api endpoints [...]

So to implement this logic I think I need to verify the JWT token client-side

If I understood you correctly you are talking about checking if a JWS has expired in the client side. In order to do this you don't need to verify the token signature (although the library you are using seems to be doing both things at the same time for you, but also lets you to disable expiration control with ignoreExpiration flag). (Unless you're encrypting the claims, aka using JWE) The RFC 7515 (JWS) says nothing about expiration. Message Signature or MAC Validation doesn't control expiration (and it shouldn't because signatures gives you authenticity and integrity). Even the RFC 7519 (JWT) doesn't control the expiration claim for resolve if a JWT is valid or not.

Also, all the claims are optional.

So, you could check if a JWT has expired or not without verifying the signature, hence you don't need neither a public key (for asymmetric encryption like RSA) or a secret key (for symmetric encryption like AES). In JWT and JWS tokens, the claims are just plaintext base64 encoded so you could just decode the payload without verifying if the signature is valid and read the expiration claim. If you are encrypting the payload (aka using JWE) then you will not be able to do this.

A note from jjwt library

JWTs can be cryptographically signed (making it a JWS) or encrypted (making it a JWE).

Here is a ligthweigth library from auth0 to decode the base64encoded claims of a JWT/JWS token. A guy is even asking about checking expiration.

I don't know why you think that you should be doing this control client-side, the only advantage is avoiding sending API request that the client knows that will fail. And they should fail because the server should be validating that the token hasn't expired, previous signature verification (with secret/private key) obviously.

The RFC 7519 says about this claim:

The "exp" (expiration time) claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing.

In a web app like the one you say the use of tokens is to allow stateless servers to authenticate client requests. The goal of the OPTIONAL expiration claim is to allow the server have some control over the generated JWS (if we are using JWT for authentication signing them is a must so we should be talking about JWS).

Without expiration, the tokens will be valid forever or until the key used to signing them change (this will make the verification process to fail). By the way, invalidating sessions is one of the most notorious disadvantages of using stateless authentication.

Session invalidation becomes a real problem if we are including information in the JWS payload (aka claims) used for authorization, for example which roles the user have.

From Stop using JWT for sessions

but more seriously, it can also mean somebody has a token with a role of admin, even though you've just revoked their admin role. Because you can't invalidate tokens either, there's no way for you to remove their administrator access

The expiration control doesn't solve this problem and I think is more oriented to avoid session hijacking or CSRF attacks.

An attacker using CSRF will be able to make a request with an expired JWS to your API skipping the expiration control.

A different issue is verifying the signature in the client using the public or secret key.

Regarding your question

I am using seems to require a public key to use it's verify() function. I don't seem to have a public key, only a secret, which I just made up, so it wasn't generated with a pair.

The verify method you pointed out says explicitlly that it accepts a public or secret key.

jwt.verify(token, secretOrPublicKey, [options, callback])

secretOrPublicKey is a string or buffer containing either the secret for HMAC algorithms, or the PEM encoded public key for RSA and ECDSA

I assume you are using neither and you are using a string like 'shhhh'.

var token = jwt.sign({ data: '¿Donde esta Santiago?'}, 'shhhh');

Then you should do

var decoded = jwt.verify(token, 'shhhhh');

However, the question here is: Is client-side signature verification really needed?

I think is not, at least not for this kind of application where the client just uses the JWS to send subsequent request to the server saying: "Hey server, I'm Gabriel and I have a paper (token) here that assures that and that paper is signed by you." So if the client doesn't validate the JWS and a MITM had successfully gave to that client a JWS signed by himself (instead to the JWS signed by the server), then the subsequent request will simply fail. Like expiration control, signature verification only prevent the client to make request that will fail.

Now, client side verification requires sending the public or secret key. Sending public key doesn't represent a security concern but it's extra effort and processing with little benefits.

Sending secret keys (like 'shhhh') can represent a security issue because is the same key that is used to sign tokens.

这篇关于JWT 验证客户端?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆