JWT验证客户端?

时间:2017-05-04 16:18:44

标签: node.js typescript jwt passport.js json-web-token

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

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

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

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

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

Q2,我使用的JWT库似乎需要公钥来使用它的verify()功能。我似乎没有公钥,只有一个秘密,我刚刚组成,所以它不是用一对产生的。我的公钥来自哪里,或者有没有其他方法来验证我的令牌?

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

5 个答案:

答案 0 :(得分:14)

<强> TL; DR

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

    我现在意识到,在令牌过期后,我的前端仍然允许用户请求我的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),那么您将无法执行此操作。

    来自jjwt library

    的说明
      

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

    Here是来自auth0的 ligthweigth库,用于解码JWT / JWS令牌的base64encoded声明。 一个人甚至询问checking expiration

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

    RFC 7519说了这个说法:

      

    &#34; exp&#34; (到期时间)声明标识到期时间      或者在此之后JWT 不得接受处理

    在类似的网络应用程序中,您说使用令牌是允许无状态服务器对客户端请求进行身份验证。 可选到期声明的目标是允许服务器对生成的JWS进行一些控制(如果我们使用JWT进行身份验证签名是必须的,那么我们应该讨论JWS)。

    如果没有过期,令牌将永久有效或直到用于签名的密钥发生更改(这将使验证过程失败)。 顺便说一下,invalidating sessions是使用无状态身份验证的最臭名昭着的缺点之一。

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

    来自Stop using JWT for sessions

      

    但更严重的是,它也可能意味着某人拥有一个具有管理员角色的令牌,即使您刚刚撤销了他们的管理员角色。由于您无法使令牌无效,因此无法删除其管理员权限

    到期控制并没有解决这个问题,我认为更倾向于避免会话劫持或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;

  1. 每当用户登录时,生成访问权限并刷新令牌并返回给用户这样的内容;

    { "accessToken": <<accessToken>> "refreshToken": <<refreshToken>> "expiresAt": <<expiresAt>> }

    因此,客户端可以了解访问令牌何时到期,并可以使用刷新令牌刷新它。

  2. 加密您在访问令牌中放置的数据,因为有机会在没有密钥的情况下访问数据。但当然有人需要秘密密钥来验证。

答案 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)并且验证/签名确实需要它