如果你能解码JWT它们如何安全?

时间:2014-12-04 18:42:16

标签: security jwt express-jwt

我爱JWT。与之合作真的很有趣。我的问题是:如果我得到JWT并且我可以解码有效载荷,那么它是如何安全的?我是不是只是从标题中取出令牌,解码并更改有效负载中的用户信息,然后使用相同的正确编码密码将其发回?

我知道他们必须是安全的,但我真的很想了解这些技术。我错过了什么?谢谢!

10 个答案:

答案 0 :(得分:276)

JWT可以是签名,加密或两者兼而有之。如果令牌已签名但未加密,则每个人都可以阅读令牌的内容,但是当您不知道私钥时,您无法对其进行更改。否则,接收方将注意到签名不再匹配。

回答你的评论:我不确定我是否以正确的方式理解你的评论。只是为了确定:你知道并理解数字签名吗?我只是简单地解释一个变体(HMAC,它是对称的,但还有很多其他变体)。

让我们假设Alice想要向Bob发送JWT。他们都知道一些共同的秘密。马洛里并不知道这个秘密,但想干涉和改变JWT。为了防止这种情况,Alice计算Hash(payload + secret)并将其作为签名附加。

当收到消息时,Bob还可以计算Hash(payload + secret)以检查签名是否匹配。 但是,如果Mallory更改了内容中的某些内容,则她无法计算匹配的签名(可能是Hash(newContent + secret))。她不知道这个秘密,也无法找到它。 这意味着如果她改变了某些东西,签名就不再匹配了,Bob就不再接受JWT了。

我们假设,我向其他人发送了{"id":1}消息,并使用Hash(content + secret)对其进行了签名。 (+只是这里的连接)。我使用SHA256哈希函数,我得到的签名是:330e7b0775561c6e95797d4dd306a150046e239986f0a1373230fda0235bda8c。现在轮到你了:扮演Mallory的角色并尝试签署消息{"id":2}。你不能,因为你不知道我使用了哪个秘密。如果我认为收件人知道秘密,他可以计算任何邮件的签名并检查它是否正确。

答案 1 :(得分:88)

您可以转到jwt.io,粘贴您的令牌并阅读内容。这对最初很多人来说很不利。

简短的回答是JWT不关心加密。它关心验证。也就是说,它总能得到“让这个令牌的内容被操纵”的答案?这意味着用户操纵JWT令牌是徒劳的,因为服务器将知道并忽略令牌。在向客户端发出令牌时,服务器会根据有效负载添加签名。稍后它会验证有效负载和匹配签名。

合乎逻辑的问题是不关心加密内容的动机是什么?

  1. 最简单的原因是因为它假设这在很大程度上是一个已解决的问题。例如,如果处理类似Web浏览器的客户端,则可以将JWT令牌存储在secure + httpsOnly的cookie中(Javascript无法读取,无法通过HTTP读取)并与服务器进行通信通过加密通道(HTTPS)。一旦您知道服务器和客户端之间有安全通道,您就可以安全地交换JWT或其他任何您想要的内容。

  2. 这样可以保持简单。一个简单的实现使得采用更容易,但它也让每个层都做它最擅长的事情(让HTTPS处理加密)。

  3. JWT并不意味着存储敏感数据。一旦服务器收到JWT令牌并对其进行验证,就可以在其自己的数据库中查找用户ID,以获取该用户的其他信息(如权限,邮政地址等)。这使JWT的尺寸变小,避免了无意中的信息泄漏,因为每个人都知道不要在JWT中保留敏感数据。

  4. 与Cookie本身的工作方式并无太大差别。 Cookie通常包含未加密的有效负载。如果您使用HTTPS,那么一切都很好。如果您不是,那么建议您自己加密敏感cookie。不这样做意味着可能发生中间人攻击 - 代理服务器或ISP读取cookie,然后假装是你,然后重放它们。出于类似的原因,JWT应始终通过HTTPS等安全层进行交换。

答案 2 :(得分:54)

让我们从一开始就进行讨论:

JWT是一种非常现代,简单且安全的方法,可扩展到Json Web令牌。 Json Web令牌是身份验证的无状态解决方案。因此,无需在服务器上存储任何会话状态,这对于静态API来说是完美的选择。 Restful API应该始终是无状态的,并且使用JWT进行身份验证的最广泛使用的替代方法是仅使用会话将用户的登录状态存储在服务器上。但是,那当然不遵循说宁静的API应该是无状态的原则,这就是为什么JWT之类的解决方案变得流行和有效的原因。

因此,现在让我们知道身份验证实际上如何与Json Web令牌一起使用。假设我们已经在数据库中有一个注册用户。因此,用户的客户端首先发出带有用户名和密码的发布请求,然后应用程序检查用户是否存在,如果密码正确,则应用程序将仅为该用户生成唯一的Json Web令牌。

使用存储在服务器上的秘密字符串创建令牌。接下来,服务器然后将该JWT发送回客户端,客户端会将其存储在cookie或本地存储中。 enter image description here

就像这样,对用户进行身份验证并基本上登录到我们的应用程序中,而不会在服务器上保留任何状态。

因此,服务器实际上并不知道实际登录的用户,但是,当然,用户知道他已登录,因为他具有有效的Json Web令牌,该令牌有点像通行证,可访问受保护的部分。应用。

再说一遍,只是为了确保您明白了。用户一旦取回其唯一的有效Json Web令牌(未保存在服务器上的任何位置),便立即登录。因此,此过程是完全无状态的。

然后,例如,每次用户想要访问受保护的路由(例如,其用户个人资料数据)时。他将Json Web令牌和请求一起发送,所以这有点像出示护照以访问该路由。

一旦请求到达服务器,我们的应用程序将验证Json Web令牌是否真的有效,并且如果用户确实是他说的真实身份,那么所请求的数据将被发送到客户端,如果不是,就会出现一个错误,告诉用户不允许他访问该资源。 enter image description here

所有这些通信必须通过https进行,因此请使用安全加密的Http,以防止任何人都可以访问密码或Json Web令牌。只有这样我们才能拥有真正安全的系统。

enter image description here

因此,Json Web令牌看起来像此屏幕快照的左部分,该屏幕截图取自jwt.io的JWT调试器。因此,从本质上讲,它是由三个部分组成的编码字符串。标头,有效载荷和签名现在,标头只是有关令牌本身的一些元数据,有效载荷是我们可以编码为令牌的数据,也就是我们真正想要的任何数据。因此,我们要在此处编码的数据越多,JWT越大。无论如何,这两个部分只是纯文本,将被编码,但不会被加密。

因此任何人都可以对其进行解码和读取,我们无法在此处存储任何敏感数据。但这根本不是问题,因为在签名的第三部分中,事情确实变得很有趣。签名是使用标头,有效负载和服务器上保存的机密创建的。

然后将整个过程称为签署Json Web令牌。签名算法采用标头,有效负载和秘密来创建唯一的签名。所以只有这些数据加上秘密才能创建此签名,好吗? 然后,这些签名连同标头和有效负载一起组成JWT, 然后将其发送给客户端。 enter image description here

一旦服务器接收到JWT来授予对受保护路由的访问权,它就需要对其进行验证,以确定用户是否确实是他声称的身份。换句话说,它将验证是否没有人更改令牌的标头和有效载荷数据。同样,此验证步骤将检查是否没有第三方实际更改Json Web令牌的标头或有效载荷。

那么,此验证实际上如何工作?好吧,这实际上很简单。收到JWT后,验证将获取其标头和有效负载,以及仍保存在服务器上的机密信息,基本上会创建一个测试签名。

但是第一次创建JWT时生成的原始签名仍然在令牌中,对吗?这就是验证的关键。因为现在我们要做的就是将测试签名与原始签名进行比较。 并且,如果测试签名与原始签名相同,则表示有效负载和标头尚未修改。 enter image description here

因为它们已经被修改,所以测试签名必须是不同的。因此,在这种情况下,数据没有发生变化,我们可以对用户进行身份验证。当然,如果两个签名 实际上是不同的,那么,这意味着有人篡改了数据。 通常通过尝试更改有效负载。但是,操纵有效载荷的第三方当然不能访问该秘密,因此它们无法签署JWT。 因此,原始签名将永远不会对应于被操纵的数据。 因此,在这种情况下,验证将始终失败。这是使整个系统正常工作的关键。是让JWT变得如此简单的魔力, 但也非常强大。

答案 3 :(得分:12)

json Web令牌(JWT)中的内容本质上不安全,但有一个用于验证令牌真实性的内置功能。 JWT是由句点分隔的三个哈希。第三是签名。在公钥/私钥系统中,发行者使用私钥对令牌签名进行签名,该私钥只能通过其对应的公钥进行验证。

理解发行者和验证者之间的区别非常重要。令牌的接收者负责验证它。

在Web应用程序中安全地使用JWT有两个关键步骤:1)通过加密通道发送它们; 2)在收到签名后立即验证签名。公钥加密的非对称性使得JWT签名验证成为可能。公钥验证JWT是否由其匹配的私钥签名。没有其他密钥组合可以执行此验证,从而防止模拟尝试。按照这两个步骤,我们可以确保数学上确定JWT的真实性。

更多阅读:How does a public key verify a signature?

答案 4 :(得分:4)

我会用一个例子来解释这一点。

假设我向你借了 10 美元,然后我给了你一张带有我签名的借据。每当您或其他人将这张借条还给我时,我都会还给您,我会检查签名以确保这是我的。

我不能保证你不会把这张借条的内容展示给任何人,甚至不会把它交给第三者,我只关心这张借条是我签名的,当有人把这张借条给我看的时候,要我付钱。

JWT 的工作方式完全相同,服务器只能确保收到的令牌是自己发出的。

您需要其他措施来确保其安全,例如使用 HTTPS 传输加密、确保存储令牌的本地存储安全、设置来源。

答案 5 :(得分:2)

只有JWT的privateKey会在您的服务器上解密加密的JWT。知道privateKey的人将能够解密加密的JWT。

将privateKey隐藏在服务器的安全位置,永远不要告诉任何人privateKey。

答案 6 :(得分:2)

Ref - JWT Structure and Security

请务必注意,JWT用于授权而非身份验证。 因此,只有在您通过服务器身份验证并通过指定凭据后才能为您创建JWT。一旦为所有将来与服务器JWT的交互创建了JWT,就可以使用它。因此,JWT告诉服务器该用户已通过身份验证,如果他具有该角色,则让他访问特定资源。
JWT有效负载中的信息对所有人都是可见的。可能会有“中间人”攻击,并且JWT的内容可以更改。因此,我们不应在有效负载中传递任何敏感信息,例如密码。如果我们想使有效载荷数据更安全,则可以对其进行加密。如果有效负载被服务器篡改,则会识别出来。
因此,假设用户已通过身份验证并提供了JWT。生成的JWT具有声明为 Admin 角色的声明。签名也是通过

生成的

enter image description here

此JWT现在已被篡改,并假设 角色更改为超级管理员
然后,当服务器收到此令牌时,它将再次使用密钥(只有服务器拥有)和有效负载来生成签名。它将不匹配签名 在JWT中。因此服务器将知道JWT已被篡改。

答案 7 :(得分:0)

我建议使用jwt.io中不存在的特殊算法来解密JWE

参考链接:https://www.npmjs.com/package/node-webtokens

jwt.generate('PBES2-HS512+A256KW', 'A256GCM', payload, pwd, (error, token) => {
  jwt.parse(token).verify(pwd, (error, parsedToken) => {
    // other statements
  });
});

这个答案可能为时已晚,或者您可能已经找到了答案,但是我仍然认为这对您和其他人也有帮助。

我创建的一个简单示例:https://github.com/hansiemithun/jwe-example

答案 8 :(得分:0)

对于像我这样负担不起昂贵数据库查询的人来说,保留敏感数据(用户权限等)的一种选择是,生成JWT时,您可以加密此数据并将其附加到JWT令牌。 (将加密密钥保留在后端)

当您想读取敏感信息时,可以将JWT令牌发送到后端并对其解密,然后将其取回。这样,您不必进行数据库查找或通过JWT令牌将敏感信息裸露在前端

答案 9 :(得分:-19)

JWT中的数据经过签名和加密,并不意味着它是安全的。 JWT不为敏感数据提供保证。

使用私钥加密数据,该私钥是双方都知道的,即发送方和接收方,入侵者可以制作密钥并可以更改内容。

据我所知,JWT不提供安全性。

由于