如果您可以解码 JWT,它们的安全性如何?

Posted

技术标签:

【中文标题】如果您可以解码 JWT,它们的安全性如何?【英文标题】:If you can decode JWT, how are they secure? 【发布时间】:2020-08-24 09:11:04 【问题描述】:

如果我得到一个JWT 并且我可以解码有效负载,那么它的安全性如何?难道我不能从标头中获取令牌,解码并更改有效负载中的用户信息,然后使用相同的正确编码秘密将其发回吗?

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

【问题讨论】:

md5('original messaged' + secret) != md5('changed message' + secret) 因此,如果有人更改了消息,您可以检测到它 在理想情况下为真,但是,md5 有冲突。 @Pithikos @YashKumarVerma 是的,这只是为了展示它的要点,因为每个人都知道 md5。 @user1955934 它是 base64 编码的,加密。您可以简单地使用任何 base64 解码器对其进行解码。 所以客户端需要同时发送哈希和 jwt 令牌?稍后在服务器端,他们将尝试使用秘密对 jwt 令牌进行哈希处理并与哈希值进行比较? 【参考方案1】:

JWT 可以是签名的、加密的或两者兼有。如果一个令牌是签名的,但没有加密,每个人都可以读取它的内容,但是当你不知道私钥时,你就无法更改它。否则,接收方会注意到签名不再匹配。

回答您的评论:我不确定我是否正确理解了您的评论。只是为了确定:您知道并理解数字签名吗?我将简要解释一种变体(HMAC,它是对称的,但还有许多其他变体)。

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

Bob 收到消息时也可以计算Hash(payload + secret) 来检查签名是否匹配。 但是,如果 Mallory 更改了内容中的某些内容,她将无法计算匹配签名(即 Hash(newContent + secret))。她不知道这个秘密,也没有办法找出来。 这意味着如果她更改某些内容,签名将不再匹配,Bob 将不再接受 JWT。

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

【讨论】:

那么在payload改变的时候签名也改变了?我的印象是令牌的格式为 [header].[payload].[signature] 是由有效负载和秘密组合计算的签名吗?如果是这种情况,那么具有不同 id 的有效负载是否与该秘密相同?就像数据是 id:1 并且用于计算带有秘密的令牌的签名部分,这是否意味着 id:2 对用户 2 有效,因此用户 1 可以更改id 为 2 并且令牌会相同? 哦,我现在明白了。我不知道为什么我错过了当您更改有效负载时秘密散列不正确的想法,因为必须重新计算秘密散列。出于某种原因,我仍然认为它是独立的。最后一点真的把它钻回家了。感谢您引导我完成它。 我有一个相关的问题。是什么阻止某人使用复制的 JWT 冒充 Alice? 如果有人拥有 JWT,他们可以冒充 Alice。所以你需要小心你如何存储和发送它。您还应该在有效负载中为其设置过期时间。这样,如果有人窃取了 JWT,他们使用它的时间是有限的。看看stormpath.com/blog/… @Morrowless 我遇到了用户“共享”令牌的问题。我们为提高安全性所做的就是使用用户的 IP 地址作为解密过程的一部分。因为这发生在后端;用户可以与另一个用户共享令牌,但不知道为什么会被拒绝。同时,我们销毁/使共享令牌无效,因此两个用户都必须再次登录并获得新令牌。当然,这并不完美,因为如果您连接到同一个网络,您仍然可以摆脱它。我希望看到的是通过标头传递的浏览器强制会话 ID。【参考方案2】:

您可以前往jwt.io,粘贴您的令牌并阅读内容。起初这让很多人感到不安。

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

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

    最简单的原因是因为它假设这在很大程度上是一个已解决的问题。例如,如果与 Web 浏览器之类的客户端打交道,您可以将 JWT 令牌存储在 secure(不通过 HTTP 传输,仅通过 HTTPS)和 httpOnly(无法被 javascript 读取)的 cookie 中并通过加密通道 (HTTPS) 与服务器对话。一旦你知道你在服务器和客户端之间有一个安全通道,你就可以安全地交换 JWT 或任何你想要的东西。

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

    JWT 并非用于存储敏感数据。一旦服务器接收到 JWT 令牌并对其进行验证,它就可以在自己的数据库中查找用户 ID 以获取该用户的其他信息(如权限、邮政地址等)。这样可以使 JWT 保持较小的大小并避免无意的信息泄露,因为每个人都知道不要将敏感数据保存在 JWT 中。

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

【讨论】:

注意! JWT 应始终通过 HTTPS 等安全层进行交换 但是如果 JWT 仅通过 HTTPS 是安全的,为什么不直接发送有效负载呢? POST -> 用户名、密码。它仍然是加密的吧? @GeekPeek 你应该阅读 JWT 基础知识,但像你提到的 Session Auth 通常是你所需要的。 JWT 提供了一些其他好处,但也做了一些权衡webskeleton.com/webdev/2019/10/22/… 你只是读懂了我的想法,事实上,作为我的新手,我很惊讶 jwt.io 可以读取我的有效载荷,那么签名到底在做什么?现在我最大的问题是如果你的 JWT 被盗了怎么办?!!! @aleemb,如果黑客不调整 jwt 而是使用相同的 jwt 来制作和检索一些重要数据,它的安全性如何?服务器不会相信这是一个真正的请求吗?还是我缺少什么?【参考方案3】:

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

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

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

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

【讨论】:

【参考方案4】:

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

将私钥隐藏在服务器的安全位置,切勿将私钥告诉任何人。

【讨论】:

JWT 并不总是加密的。它们可以被签名、加密、签名后加密或加密后签名。【参考方案5】:

让我们从头开始讨论:

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

现在让我们了解身份验证如何与 Json Web 令牌一起工作。假设我们的数据库中已经有一个注册用户。因此,用户的客户端首先使用用户名和密码发出 post 请求,然后应用程序检查用户是否存在以及密码是否正确,然后应用程序将为该用户生成唯一的 Json Web Token。

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

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

所以服务器实际上并不知道哪个用户实际登录了,但当然,用户知道他已经登录了,因为他有一个有效的 Json Web 令牌,有点像访问受保护部分的通行证应用。

再说一遍,只是为了确保您明白这一点。用户在取回其唯一的有效 Json Web 令牌后立即登录,该令牌未保存在服务器上的任何位置。所以这个过程是完全无状态的。

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

一旦请求到达服务器,我们的应用程序将验证 Json Web Token 是否真的有效,如果用户真的是他所说的那个人,那么请求的数据将被发送到客户端,如果不是,然后会有一个错误告诉用户他不允许访问该资源。

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

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

因此任何人都可以对其进行解码和阅读,我们不能在此处存储任何敏感数据。但这根本不是问题,因为在第三部分,所以在签名中,才是真正有趣的地方。签名是使用标头、有效负载和保存在服务器上的秘密创建的。

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

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

那么,这种验证实际上是如何工作的?嗯,它实际上很简单。一旦接收到 JWT,验证将获取它的 header 和 payload,以及仍然保存在服务器上的 secret,基本上创建了一个测试签名。

但是当初创建 JWT 时生成的原始签名还在令牌中,对吧?这就是验证的关键。因为现在我们要做的就是将测试签名与原始签名进行比较。 而如果测试签名与原始签名相同,则说明payload和header没有被修改。

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

【讨论】:

嗨@Lord,这太棒了!我不知道为什么它的票数这么少! hi @ani0904071,在下面的链接中你会找到一个 node.js 的例子,我希望它也会有所帮助。 ***.com/questions/31309759/… 这个答案很棒!以非常简化的形式,如果 JWT 是“2.5.9”(header=2,payload=5,signature=9),那么篡改它并将数据更改为 6 将不起作用。假设服务器的秘密乘以 header*payload 并减去 1。“测试签名”将是 2*6-1=11。但是 JWT 的签名是 9,所以服务器知道有东西被篡改了。理解起来很容易,但由于某些原因,许多文章都忽略了低级的东西,这使它看起来比实际更神奇和复杂。 谢谢,现在我明白了。如此简单,却又如此难以掌握。现在看来微不足道了。 问题不在于修改 JWT 时会发生什么。问题是当 B 可以访问 A 的 JWT 时会发生什么(例如,他们坐在一起,A 去厕所,B 在 A 的 chrome 中打开开发工具并从本地存储复制粘贴 JWT 并发送它到他的电脑),并使用它在 API 中发出调用。如何判断来自 B 个人电脑(但实际上是发给 A)的请求无效?【参考方案6】:

Ref - JWT Structure and Security

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

生成的

这个 JWT 现在被篡改了,假设 角色更改为 超级管理员 然后,当服务器收到此令牌时,它将再次使用密钥(只有服务器拥有)和有效负载生成签名。它与签名不匹配 在智威汤逊。这样服务器就会知道JWT被篡改了。

【讨论】:

【参考方案7】:

我会用一个例子来解释。

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

我不能确保你不向任何人展示这张借条的内容,甚至不把它交给第三人,我只关心这张借条是由我签署的,当有人向我展示这张借条并且让我付钱。

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

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

【讨论】:

完美的比喻! > 使用HTTPS传输加密,确保存储令牌的本地存储是安全的,设置来源

以上是关于如果您可以解码 JWT,它们的安全性如何?的主要内容,如果未能解决你的问题,请参考以下文章

如果您可以解码 JWT,它们的安全性如何?

如果您可以解码 JWT,它们的安全性如何?

JWT 和类似方法的安全性如何?

使用 JWT 刷新令牌如何安全?

为啥我不需要私钥来解码我的亚马逊 JWT?

编码 JWT 的原因是啥?