使用JWT确保应用程序安全的简要介绍
在实现用户认证时,JSON网络令牌已经成为现代开发者最喜欢的选择。JWT的流行显然是由它给应用开发带来的好处所决定的。在这篇文章中,我们将深入研究JWT,以及它们为什么在其他认证选项中脱颖而出,还有在使用它们时应该注意的问题。
什么是JWT令牌?
JWT是一种独立的方法,可用于在两个端点之间安全地传输数据。JWTs最常用于用户认证。它们也可以用来安全地交换信息。在这篇文章中,我们将介绍JWTs如何用于用户认证。然而,使用JWT交换信息的步骤与用户认证大致相同。
涉及JWTs的认证过程遵循这些步骤。当用户第一次登录应用程序时,系统后端向用户发出一个JWT,并将其发送到客户端。这个令牌包含一个特殊的签名,验证该令牌是由系统发出的。客户端将令牌存储在浏览器中,并在每次请求时将其发送给服务器,在那里令牌被用来验证用户的认证。
JWT由3个字符串组成,用句号分隔。这3个字符串是头、有效载荷和签名。下面是一个由这三部分组成的JWT令牌的例子:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJteXdlYnNpdGUuY29tIiwiaWF0IjpudWxsLCJleHAiOjUxNDM3ODA4MDAwLCJhdWQiOiIiLCJzdWIiOiIiLCJpZCI6IjEyMzQ1OTEiLCJuYW1lIjoiTWFyeSBQb3BwaW5zIiwicm9sZSI6ImVkaXRvciJ9.LYKHdyKj6SnxYaRZH_ZhiW6yk31zaBQehYm4BgawH_o

让我们看看这些部分对令牌的整体构成有何贡献。
头部
JWT头包含JSON格式的令牌的元数据。头中的两个字段是alg和typ。'alg'指定了生成签名时用于签署令牌的算法,我们稍后将讨论这个问题。typ'指定了令牌的类型,是'JWT'。一个典型的令牌头在下面的例子中显示:
{
"alg": "RS256",
"typ": "JWT"
}
在这里,标头指出用来签署令牌的算法是RS256。
头部在被base64url编码后被存储为令牌的第一部分。
有效载荷
JWT的有效载荷以JSON格式存储关于令牌和任何其他实体的信息。通常,用于认证的JWT会存储一些关于用户的关键信息,如用户ID和用户角色。存储用户信息的令牌通常看起来像这样:
{
"id": "1234591",
"name": "Mary Poppins",
"role": "editor"
}
这些存储在有效载荷中的JSON字段被称为索赔。
此外,还有一些索赔是由JWT标准定义的。没有必要在JWT中包括所有这些权利要求,但在大多数情况下至少包括其中的一些权利要求是有益的。下面是我们可以使用的几个标准声明:
- iss。定义了令牌的发行者。
- exp。为令牌提供一个过期时间。一旦过了这个过期时间,该令牌就不再有效。
- aud:定义了令牌的受众。
- iat:存储令牌的发行时间。
让我们看看带有一些标准要求的有效载荷是什么样子的:
{
"id": "1234591",
"name": "Mary Poppins",
"role": "editor",
"iss": "mywebsite.com",
"exp": 3600
}
JWT的有效载荷可以包括任何你想要的信息字段,但建议尽可能地保持其大小。此外,你不应该在有效载荷中存储敏感信息,如用户密码,因为它没有被加密。它只是以base64url编码的方式进行编码。
签名
JWT令牌的最后一部分,即签名,是一个消息验证码,用来验证令牌没有被修改,也没有被除授权应用服务器以外的外人生成。
签名是通过使用一种加密算法和存储在服务器中的秘密对JWT头和有效载荷的组合进行签名而产生的。只有拥有令牌的头和有效载荷以及秘密的人,才能生成一个被服务器接受的签名。因此,使用一个强大的秘密来加密令牌并安全地存储在服务器中是很重要的。
如果我们使用HMAC SHA-256这样的对称算法,发布JWT的服务器和验证JWT的服务器应该能够安全地访问该秘密。如果使用RS256这样的非对称算法,我们可以使用公钥-私钥系统,用私钥来签署令牌,用公钥来验证令牌。
正如你已经猜到的,签名是JWT令牌的最关键部分。它将未经认证的来源挡在应用程序之外,并保持一切安全。当发出的JWT与客户端的每个请求一起被送回服务器时,服务器会检查签名以验证它是由系统本身发出的令牌,然后继续为客户端请求提供服务。

JWT有什么特别之处?
当我们了解了JWT令牌的构成后,你可能已经在想,与其他认证方法相比,尤其是基于会话的认证,是什么让它如此特别。
JWT最重要的特点是它的无状态性。我们在上一节中看到了JWT中可以存储什么样的数据,所有验证令牌和识别用户所需的数据都存储在令牌本身。不需要在服务器中维护令牌的任何记录,比如像我们对会话那样将令牌存储在数据库中。这使得JWT是无状态的。
这种无状态的性质给我们带来了JWTs的最大好处。发布JWT的服务器和验证JWT的服务器不一定是同一台。一台服务器可以发布JWT并执行验证任务,而另一台实现应用逻辑的服务器可以独立于第一台服务器验证JWT。
在API后端和微服务架构应用的时代,这使得开发人员可以将所有的认证任务委托给一台服务器,而其他服务器则实现应用逻辑,并尽可能地将系统解耦。
当JWT的发布和验证由不同的服务器进行时,使用公钥-私钥系统是最好的方法。在这种情况下,私钥应安全地存储在验证服务器中。
如何从客户端向服务器发送JWT令牌,反之亦然?
在服务器和客户端之间来回发送JWT时,我们可以将其与授权的HTTP头一起发送。然而,通过HTTP连接发送令牌使其容易受到中间人(MITM)攻击和被盗令牌。因此,在使用JWTs时,必须使用安全的HTTP连接。
在HTTP头中发送令牌的另一个问题是,在这种方法中,令牌通常存储在客户端浏览器的本地存储中。这使得令牌有可能被跨站脚本(XSS)攻击所窃取。
作为一个解决方案,你可以在一个cookie中发送令牌,而不是在授权头中。设置HttpOnly和cookie的安全标志,以防止攻击者利用XSS攻击窃取令牌,这一点很重要。
使你的JWT令牌过期
如果JWT令牌以某种方式被第三方窃取,他们可以使用窃取的令牌来访问应用程序,并获得令牌所有者的权限。作为这个问题的解决方案,我们可以为JWTs设置一个较短的过期时间。这样,即使攻击者偷了一个令牌,他们也不能在足够长的时间内利用它来产生不良的影响。
我们可以在有效载荷内设置exp claim,给令牌一个较短的过期时间。然而,这个动作并不能消除被盗代币的威胁,只是减少了它导致严重攻击的机会。
在现实世界的应用中,为JWTs设置一个较短的过期时间并不像听起来那么简单。如果我们创建的令牌在创建十分钟后就过期了,这将大大降低你的应用程序的用户体验。用户有多大可能容忍他们在你的应用程序上每花10分钟就必须重新登录一次?
但有一个解决方案:刷新令牌。
刷新标记--它们是什么?
由于要求用户在每次JWT令牌在短时间内过期后重新登录并不是一个好的解决方案,开发者已经在刷新令牌中找到了一个解决方案。
刷新令牌是一个具有较长过期时间的JWT令牌。它被用来在每次旧的访问令牌过期时发出一个新的访问令牌,这也是一个JWT令牌,但过期时间更短。
在这种情况下,访问令牌是在客户端和服务器之间来回发送的令牌,并且有很短的过期时间。然而,当访问令牌在短时间内过期时,服务器不是要求用户再次登录,而是使用刷新令牌来生成新的访问令牌。刷新令牌存储了创建新访问令牌所需的数据。
由于刷新令牌有很长的过期时间,而且它在任何情况下都不会被传递到客户端,我们应该把它们存储在后台数据库中。如果攻击者以某种方式获得了刷新令牌,鉴于其过期时间较长,这对系统和刷新令牌的所有者是一个严重的安全威胁。因此,刷新令牌必须在最大的安全措施下存储。
用户在登录系统时首先收到一个刷新令牌。然后,该令牌被存储在一个安全的数据库中。每次生成新的访问令牌时,都会发放一个新的刷新令牌,这是你可以做的,以确保被盗刷新令牌的安全影响很低。
当刷新令牌过期时,用户必须再次登录到应用程序并获得新的刷新令牌。
如果JWT的发行和验证是由不同的服务器处理的,那么需要注意的是,使用刷新令牌发行访问令牌是一项由认证服务器处理的任务。
总结
JWT是一种现代的、强大的解决方案,用于验证用户和分享敏感信息,同时不保持状态。一个JWT由三部分组成:头、有效载荷和签名。尽管JWT在现代应用开发中的好处,但我们需要采取一些特别的措施来确保令牌和用户数据的安全。在cookie中而不是在头中发送JWT,缩短其过期时间,以及使用刷新令牌来发行新的访问令牌,是我们可以采取的一些安全措施,以保证我们的应用程序、其用户和他们的数据的安全。
在后续的文章中,我们将在一个API上实现所有这些。
谢谢你的阅读!