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

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

问题描述

我有一个带角度前端的nodejs api.该API已成功将JWT与护照一起使用以保护其端点.

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

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

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

因此,要实现此逻辑,我认为我需要验证JWT令牌客户端. Q1,这是一个明智的方法.

第二季度,我正在使用的 JWT 库似乎需要使用公共密钥它是verify()函数.我似乎没有公用密钥,只有一个秘密,这是我刚刚编造的,因此它不是一对生成的.我的公钥是从哪里来的,或者没有其他方法可以验证我的令牌?

这一切看起来应该很明显,我已经错过了一些东西,如果这是一个愚蠢的问题,我深表歉意,但是我似乎找不到答案?

解决方案

TL; DR

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

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

因此,要实现此逻辑,我认为我需要验证JWT令牌客户端

如果我对您的理解正确,那么您正在谈论检查客户端的JWS是否已过期. 为此,您不需要验证令牌签名(尽管您正在使用的库似乎正在执行 RFC 7515(JWS)关于有效期一无所获. 消息签名或MAC验证不能控制到期时间(并且不应该因为签名可为您提供真实性和完整性). 即使 RFC 7519(JWT)也无法控制如果JWT 有效或无效.

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

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

来自 jjwt库

的注释

JWT可以通过密码签名(使其成为 JWS )或加密(使其成为 JWE )进行加密

此处是auth0的 ligthweigth库,用于解码JWT的base64编码声明/JWS令牌. 甚至有人在问检查到期时间.

我不知道您为什么认为应该在客户端进行此控制,唯一的好处是避免发送客户端知道会失败的API请求.而且它们应该失败,因为服务器应该验证令牌还没有过期,显然是先前的签名验证(带有私钥/私钥).

RFC 7519 表示以下说法:

"exp"(失效时间)声明标识了的失效时间 或之后,不得接受JWT 进行处理.

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

在没有过期的情况下,令牌将永远有效,或者直到用于签名的令牌更改为止(这将使验证过程失败). 顺便说一句,无效 会话是该应用程序最臭名昭著的缺点之一使用无状态身份验证.

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

来自停止使用JWT进行会话

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

到期控制无法解决此问题,我认为它的定位更加合理,可以避免会话劫持或CSRF攻击.

使用CSRF的攻击者将可以使用过期的JWS向您的API发出请求,从而跳过过期控制.

另一个问题是使用公钥或秘密密钥在客户端中验证签名.

关于您的问题

我使用的似乎需要一个公共密钥才能使用它的verify()函数.我似乎没有公用密钥,只有一个秘密,这是我刚刚编造的,因此它不是一对生成的.

您指出的verify方法明确表示它接受公共密钥或秘密密钥.

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天全站免登陆