JWT简介(也包括JWS、JWE、JWA、JWK)
在过去的几年里,用户数据的安全和隐私问题一直是人们越来越关注的问题。同时,JWT作为对抗它的一种技术,也被越来越多地使用。了解JWT将使你比其他软件工程师更有优势。JWT起初可能看起来很简单,但它相当难理解。
在这篇文章中,我们将主要探讨JWT和JWS。此外,我们还将快速浏览JWE、JWA和JWK。本文旨在让读者了解JWT的概念,而不需要太过深入地探讨这个话题。
它们是什么?
在深入了解之前,我们最好先了解JWT、JWS、JWE、JWA和JWK之间的联系。
- [JSON] (dzone.com/articles/un…) 网络令牌(JWT) 是一个抽象的,以JSON网络签名(JWS)和 JSON网络加密(JWE) 的形式表示。
- JSON Web签名(JWS) 和 JSON Web加密(JWE) 使用JWA中定义的签名和加密算法。 JSON网络算法 (JWA) 中定义的签名和加密算法作为保证自身安全的方式。
- 中定义的签名算法的公钥可以被托管为 JSON Web密钥(JWK) 。
现在我们知道了它们的关系,让我们深入了解一下JWT、JWS、JWE、JWA和JWK。
JSON Web令牌(JWT)
首先,让我们看看RFC 7519中定义的JWT的定义。
"JSON Web令牌(JWT)是一种紧凑的索赔表示格式,旨在用于空间受限的环境,如HTTP授权头 和URI查询参数。JWT将索赔编码为JSON对象,作为JSON Web签名(JWS)结构的有效载荷或作为JSON Web加密(JWE)结构的明文来传输,使索赔能够被数字签名或用消息验证码(MAC)和/或加密的完整性保护。JWT总是使用JWS Compact Serialization或JWE Compact Serialization来表示"。
从这段文字中,我们可以理解JWT不是一种结构,而是一组以JWS或JWE为形式的主张,作为其自我保护的方式。在最基本的形式上,JWS和JWE之间的区别是,每个人都可以看到JWS的有效载荷,而JWE的有效载荷是加密的。
在本文中,我们将对JWS比JWE进行更多的探讨。
JSON网络算法(JWA)
JWA(RFC 7518)是JSON Web算法的缩写,它是一种规范,定义了制作JWT的散列和加密算法。
例如,以下是我们可以用来创建一个具有JWS结构的JWT的散列算法。
JSON Web Key (JWK)
JWK(RFC 7517)代表JSON Web Key。JWK是一个JSON数据结构,包含了关于散列函数的加密密钥的信息。它是一种以JSON格式存储散列密钥的方式。
JSON
{
"kty":"EC",
"crv":"P-256",
"x":"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU",
"y":"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0",
"kid":"Public key used in JWS spec Appendix A.3 example"
}
JWK通常用于托管具有非对称密钥(私钥和公钥)的散列函数的公钥,因此消费者可以自己获得密钥。
JSON网络签名(JWS)
JWS(RFC 7515)是JSON Web签名的缩写,是JWT使用的结构之一。它是JWT的最常见的实现。JWS由3部分组成:JOSE头、有效载荷和签名。
下面是一个JWS的紧凑序列化的例子。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
如果我们对JWS进行解码,我们将得到JOSE(JavaScript对象签名和加密)头。
JSON
{
"alg": "HS256",
"typ": "JWT"
}
有效载荷:JSON
JSON
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
下一个部分是签名。我们不会对它进行解码,因为它是一个字节值。
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
与JWT(在JWS结构中)有关的最常见的问题是,既然每个人都可以解码JWT并看到它的数据,那么是什么让它变得安全?JWT是安全的,因为不是每个人都能创建它,而是只有拥有秘密密钥的人才能创建。
JSON网络加密(JWE)
JWE(RFC 7516)与JWS不同,它使用一种加密算法对其内容进行加密。唯一能看到JWT内部内容的是拥有密钥的人。
JWE紧凑序列化的结构如下。
BASE64URL(UTF8(JWE Protected Header)) || ’.’ ||
BASE64URL(JWE Encrypted Key) || ’.’ ||
BASE64URL(JWE Initialization Vector) || ’.’ ||
BASE64URL(JWE Ciphertext) || ’.’ ||
BASE64URL(JWE Authentication Tag)
让我们看一下一个例子。
eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.RD09fEltrYPVNoGt2KY1Odv_5eDxkU4VX1f__P8b9zl9uzh5bmvvJy35dL-hYlUib1g63qnWBEfeSyDk5cAIQiMt6PZCBQzuWQJQlQtuo2UPLZznmLPqah37uHKB4a57q_lWf_W9soyZbO7Zj7QRNz4ZR4s5ozRHArSZcc1pAL-pYuHKyeh6Ey8t4bk66wkthjjfOjXvIfOlgbemhibegmE4GpQL6F-m0teqcAE-OxkaBRTmmb4AD5HdrCJWCIIuC52fzuWrhcoNmHM74ggtWUUjlHaKpwcVE-IWINTFaz5Pi9u4U3vnVNOZwDwB0TLSQvqnPwTZ-bYWNj8vH4TS_w.Pjo5QK1u1otxgcuBR7e8ew._OElhHugS2L6Kp04HhbFt6dLij_KXhO654RmT4JKyswYBX0wqRWt7ZzAE6eCHfJSJdMQYxqVSNloGb4OSIzYcTEo174lBZBINkHW-w2K6E0.QBDgBFizm80HLVkZvfBPCg
这个例子使用RSA_OAEP_256 密钥管理算法和AES_128_CBC_HMAC_SHA_256 作为其加密算法。如果我们对JWE进行解密,我们将得到。
JSON
{
"iss": "https://codecurated.com",
"exp": 1651417524,
"iat": 1651417224
}
解码JWT
现在让我们通过下面的例子来深入了解一下JWT。
eyJhbGciOiJIUzI1NiIsImtpZCI6IjIwMjItMDUtMDEifQ.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkJyaWxpYW4gRmlyZGF1cyIsImlhdCI6MTY1MTQyMjM2NX0.qsg3HKPxM96PeeXl-sMrao00yOh1T0yQfZa-BsrtjHI
例子中的JWT是一个具有JWS紧凑序列化结构的JWT。我们需要通过. (点)来分割JWT,并对它们进行Base64解码以查看里面的内容。
在对JWT进行分割和解码后,我们将得到三个部分。
- JOSE(JavaScript对象签名和加密)头
- 有效载荷
- 签名
JOSE头
eyJhbGciOiJIUzI1NiIsImtpZCI6IjIwMjItMDUtMDEifQ
对该字符串进行Base64解码后,我们将得到:
JSON
{
"alg": "HS256",
"kid": "2022-05-01"
}
JWT的这一部分被称为JOSE头。通过JOSE头,JWT可以告知客户端如何处理JWT。
让我们来分析一下我们的JWT中的两个字段:
alg:包含JWT的签名算法信息。kid:包含用于验证JWT的密钥的ID信息。我们将在JWK部分探讨更多关于这个问题。
alg 是唯一的强制性头,也是大多数情况下唯一需要的头,但还有很多头。
有效载荷
说完了JOSE头,让我们来看看第二部分,即有效载荷。
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkJyaWxpYW4gRmlyZGF1cyIsImlhdCI6MTY1MTQyMjM2NX0
在我们对它进行解码之后:
JSON
{
"sub": "1234567890",
"name": "Brilian Firdaus",
"iat": 1651422365
}
JWT的这一部分被称为有效载荷。有一些字段有JWT的说法(sub,name,iat)。因此,现在似乎是一个谈论JWT索赔的好时机。
有三种类型的JWT索赔:
- 注册索赔
- 公共索赔
- 私人索赔
让我们逐一分解,从注册索赔开始。注册索赔是最初在RFC 7519中记录的索赔:
iss(Issuer):表示谁是JWT的签发者。sub(Subject):表示请求JWT的用户的ID。aud(Audience):显示谁是JWT的预期消费者。exp(Expiration): JWT的过期时间。
你可以在这里查看完整的列表。正如RFC 7519规定的那样,这些要求都不是强制性的,但它们对于保证你的JWT的安全至关重要。
我们要探讨的下一种索赔类型是私人索赔。这些要求可以是任何东西。由JWT的创建者或消费者来决定索赔的名称和功能。
警告: 当指定私人索赔时,你需要注意不要在名称上造成任何碰撞。
最后一种是公共索赔,这是一种通过IETF公开注册的索赔。
大多数人没有任何用例需要他们注册他们的权利要求。对于最佳实践,你可以搜索公共权利要求的列表,并使用适合你的用例的那一个。
签名
如果你已经来到这一部分,你可能会想知道是什么让JWT变得安全,因为每个人都可以看到它的内容。好吧,JWS的有效载荷是供所有人阅读的。使JWT安全的原因是,消费者可以验证谁是发布JWT的人。
JWT中的签名部分是通过使用哈希函数创建的。如果你不熟悉哈希函数,它是一种将一个对象映射到另一个对象的算法。哈希函数有两个关键特征,使它适合于保护JWT:
- 它只以一种方式工作
- 散列过程的结果总是相同的
在这篇文章中,我们将探讨JWT中最常用的两个哈希函数:
- HMAC SHA-256
- ECDSA256
现在,让我们拆开例子中使用的签名。
qsg3HKPxM96PeeXl-sMrao00yOh1T0yQfZa-BsrtjHI
它是使用HS256 MAC算法和秘密密钥(base64格式)生成的:
7TgIAQCcYUA27bCI5+m7InRwp/mzQ+ArnFW/4c0Q51U=
HS256 MAC算法接收字节值作为其秘钥参数,并产生字节值作为其输出。因此,与标题和有效载荷不同,如果我们试图解码签名,我们将得到字节值。
让我们更深入地探讨数字签名或MAC算法。
HMAC SHA-256
我们要探讨的第一个算法是HMAC SHA-256 (HS256),这是一种使用对称密钥的哈希函数的MAC算法。带有对称密钥的散列函数意味着散列函数只有一个密钥。因此,JWT的生产者和消费者将使用相同的密钥来签署和验证JWT。使用这种算法的好处是它不需要很多CPU资源来创建散列。
信息: 建议hs256秘钥的最小字节长度为32字节。秘密密钥必须用密码学上安全的伪随机数发生器生成,以确保其随机性。
ECDSA-256
ECDSA-256 (ES256),与HMAC不同,是一种使用非对称密钥的散列函数的算法。使用非对称密钥的散列函数意味着我们需要生成两个密钥。一个密钥被称为私人密钥,它可以用来签署和验证JWT签名。另一把钥匙被称为公钥,它只能用于验证JWT签名。
顾名思义,公钥可以存储在一个公共空间(通常是JWK),所以需要验证你的JWT签名的客户端可以迅速得到它。相比之下,私钥必须是安全的,并被视为一种凭证。
选择哪种哈希函数?
我们已经学习了两种算法,HS256 和ES256 ,但是什么时候应该选择一种而不是另一种呢?你可以通过思考JWT的生产者和消费者是否不是同一个组件来轻松决定。
如果JWT的消费者是同一个组件,那么你可以使用HS256 算法。这个散列函数最常见的使用情况是当你使用JWT制作一个认证系统时。
另一方面,如果JWT是由不同的组件产生和消费的,你可以使用ES256 。这样,你就可以保护你的密钥,并确保没有其他人(甚至是公钥所有者)可以代表你创建一个JWT。
警告: 有些人认为HS256是一种反模式,因为据说JWT是用来提高安全性的,但大多数使用这种算法的用例都会降低安全性。
例如,假设你打算使用JWT作为认证会话而不是数据库。在这种情况下,你的系统更加不安全,因为你不能让JWT过期,所以你不能踢出会话。
将公钥存储为JWKs(JSON网络密钥集)
如果你打算让公众消费并验证你的JWT,建议将公钥作为JWKs托管在一个URL上。这样,如果消费者想验证你的JWT,他们可以查询托管JWKs的特定URL并获得公钥。
JSON
{
"keys": [
{
"kty": "EC",
"kid": "2022-05-01",
"x": "g_pYyqY7Htj8Aa989Ura0_mwRdqJPEnhknKzaUrztj8",
"y": "MwOFYLE-VYre92hU0iDjNx36dk7cX6xdGgdgLIPt6Ts",
"crv": "P-256"
},
{
"kty": "EC",
"kid": "2020-01-01",
"x": "6bw04ZlSMjxVzC7gXv75XAposOVTONh45ZPR0AeYaoU",
"y": "vYyCSIt0m5k4Q5A_uW8h3nEYJvgA8PgREErLcaiAHgQ",
"crv": "P-256"
}
]
}
你可能已经注意到JSON中不止一个密钥,这就是为什么它被称为JSON网络密钥集。如果你的产品使用一个以上的密钥,你可以在JWKs中托管每个密钥。为了确定使用哪个密钥,你需要在你的JOSE头中添加kid 或密钥id字段。
JSON
{
"alg":"ES256",
"kid":"2022-05-01"
}
这样,客户端就可以通过比较JOSE头JWT中的kid 和JWK中的 ,知道要获得哪个密钥。其他字段,结合起来,将构成公钥。
经验之谈
在这篇文章中,我们已经了解到:
- JWT是一个关于如何允许一方或多方安全地交换信息的抽象概念。JWT的实现是以JWS或JWE的形式出现的。
- JWS和JWE之间的区别功能是,JWS允许所有人看到它的有效载荷,而JWE通过使用加密方法不允许这样做。
- 即使每个人都能看到其有效载荷,JWS仍被认为是安全的,因为JWS的创建者可以通过使用MAC或签名验证算法的签名来验证。这样,消费者就可以确定创建 JWT 的人就是那个人。
- 我们已经探索了两种类型的算法,
HS256和ES256。当JWT的生产者和消费者是同一个组件时,HS256,而当生产者和消费者是不同的组件时,ES256。 - 我们可以使用JSON Web Key Set来承载一个非对称密钥的散列函数的公钥。你需要在JWT的JOSE头中设置
kid字段,这样消费者就可以与JWKs中的字段进行比较,得到一个兼容的密钥。
唉,我想感谢你读到最后。你可以采取的下一步是学习如何实现它。