232

最近我开始使用基于JWT的身份验证。用户登录后,将生成如下所示的用户令牌:

“eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwiwibmFtZSI6IkpvaG4gRG9lIiiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ”。

它由三个部分组成,每个部分用一个点(.)隔开。第一部分是Base64编码的头。解码后,我们将得到如下内容:

{“alg”:“HS256”,//使用的算法“典型”:“JWT”}

第二部分是声明和Base64编码。解码后,我们将得到如下内容:

{“子”:“1234567890”,“name”:“John Doe”,“admin”:true}

第三部分是签名,由以下内容生成:

HMACSHA256型(base64UrlEncode(标头)+“.”+base64UrlEncode(有效负载),*秘密base64编码*)

现在,这个密钥是什么,以及如何生成这个密钥?

我尝试了一些在线生成器,如:“http://kjur.github.io/jsjws/tool_jwt.html"

但是,我没有得到太多帮助。

10个答案10

重置为默认值
376

JSON Web令牌或JWT由三部分组成:

  1. 标头:包含一些关于令牌本身的元数据。
  2. 有效负载:包含我们想要编码到令牌中的数据,因此我们想要在这里编码的数据越多,JWT就越大。
  3. 签名。

头两部分,即头和有效载荷,只是将被编码但不加密的纯文本。

所以任何人都可以解码并阅读,我们不能在这里存储任何敏感数据。但这一点都不成问题,因为在第三部分中,签名是事情真正有趣的地方。签名是使用头、有效负载和保存在服务器上的密码创建的。

整个过程称为签署Json Web令牌。签名算法采用标头、有效负载和密钥来创建唯一签名。所以只有这些数据加上秘密才能创建这个签名。然后,这些签名与标头和有效载荷一起构成JWT,然后发送给客户端。在此处输入图像描述

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

那么,这个验证实际上是如何工作的呢?嗯,这实际上很简单。一旦接收到JWT,验证将获取其头部和有效载荷,并与仍保存在服务器上的机密一起,基本上创建一个测试签名。

但是首次创建JWT时生成的原始签名仍然在令牌中,对吗?这就是验证的关键。因为现在我们要做的就是将测试签名与原始签名进行比较。如果测试签名与原始签名相同,则表示有效负载和标头未被修改。在此处输入图像描述

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

现在让我们使用nodejs进行一些练习:

配置文件非常适合存储JWT SECRET数据。使用标准的HSA256加密进行签名,密钥至少应为32个字符长,但越长越好。

config.env:

JWT_SECRET=my-32-字符-超安全和超长机密//90天后,JWT将不再有效,即使签名者是正确的,并且一切都匹配。JWT_EXPIRES_IN=90

现在使用命令安装JWT

npm i jsonwebtoken

例如,用户注册后将JWT令牌传递给他,这样他就可以保持登录状态并访问资源。

exports.signup=catchAsync(异步(req、res、next)=>{const newUser=等待User.create({名称:req.body.name,电子邮件:req.body.email,密码:req.body.password,密码确认:req.body.passwordConfirm,});const标记=jwt.sign({id:newUser._id},process.env.jwt_SECRET{expiresIn:处理版本。JWT_EXPIRES_IN公司,});res.status(201).json({status:'成功',令牌,数据:{新用户,},});});

输出:在此处输入图像描述

在我看来,不要在第三方的帮助下生成你的超机密密钥,因为你再也不能说这是秘密了。只需使用键盘。

5
  • 7
    这应该是公认的答案,因为它包含更详细的信息 评论 2021年4月21日22:09
  • 1
    如果您也添加签名验证代码块,这将更加酷。 评论 2021年10月27日3:10
  • 1
    应该使用CSPRNG而不是键盘来生成密钥:rfc-editor.org/rfc/rfc7515#第10.1节 评论 2023年9月4日10:19
  • “在我看来,不要在第三方的帮助下生成你的超机密密钥,因为你再也不能说这是秘密了。只需使用你的键盘。”现在不建议这样做。有几篇论文表明,键盘按下的随机序列并没有人们想象的那么大的熵。这听起来可能有些夸张,但研究发现,人们倾向于遵循潜意识模式,引入可预测性,从而减少字符串的随机性。使用强大的生成器,只需选择一个经过验证/可信的本地生成器/不在第三方网站上托管的生成器。 评论 8月23日18:47
  • 这并没有回答任何问题,但它是对JWT的一个很好的概述。 评论 9月3日22:01
78

算法(高铁256)用于签署JWT意味着机密是发送方和接收方都知道的对称密钥。它是在带外协商和分发的。因此,如果您是令牌的预期接收者,发送者应该在带外向您提供秘密。

如果你是发送者,你可以使用任意的字节串作为秘密,可以生成或特意选择。你必须确保在带外向预期接收者提供秘密。

对于记录,JWT中的3个元素不是base64编码的,而是base64url-encoded,这是base64编码中的一个变体,可以产生URL-safe值。

5
  • 有办法通过一个工作示例来了解这一点吗?另外,javascript中已经考虑了答案,这也是一个很好的观点,因为问题是关于jwt的。
    – 尼隆
    评论 2020年1月23日15:52
  • 请注意,如果您正在使用JWT,则不应与任何人共享您的密钥,甚至不应与接收方(通常是您的应用程序的用户)共享密钥 评论 2021年2月17日22:16
  • 在任何情况下,我都不会给收件人发送密钥的副本。然后根据这个估计,他们可以生成自己的有效密钥散列并自己伪造令牌。密钥应该是安全的,并且只有发送方知道,以便在返回时验证令牌的内容是否有效。此外,密钥到期和轮换也是一种很好的做法。 评论 2022年3月31日13:00
  • 2
    “对称”更具体地说是指使用相同的密钥来生成和验证签名。对于web应用程序,web服务器既是生成器又是验证器。客户端只是存储令牌。在任何情况下,您都不应该与任何人共享密钥,尤其是您的web应用程序的用户。 评论 2022年5月3日19:44
  • 2
    我想,即使接受的答案有更多的细节,尤其是实践方面,我没有回答,我引用:“现在,这个密钥是什么,以及如何生成这个密钥?”。这一个是!据我所知,对于对称算法,它将是公用密钥(或任何任意字节数组),对于非对称算法,将是发送方的私钥,用作机密。 评论 1月27日10:01
67

密钥是什么

密钥与标头和有效载荷相结合,以创建一个唯一的散列。只有在拥有密钥的情况下,才能验证此散列。

如何生成密钥

您可以选择一个好的长密码。或者您可以从类似这样的站点生成.

示例(但现在不要使用此示例):

8Zz5tw0Ionm3XPZZfN0NOml3z9FMfmpgXwovR9fp6ryDIoGRM8EPHAB6iHsc0fb
4
18

您可以编写自己的生成器。密钥本质上是一个字节数组。确保转换为字节数组的字符串是base64编码的。

在Java中,您可以这样做。

字符串键=“random_secret_key”;String base64Key=DatatypeConverter.printBase64Binary(key.getBytes());byte[]secretBytes=DatatypeConverter.parseBase64Binary(base64Key);
17

密钥是做什么的,你可能已经知道了。它基本上是HMAC SH256(安全哈希)。秘密是一把对称的钥匙。

使用相同的密钥可以生成、重新验证、编辑等。

为了更安全,您可以使用私钥、公钥(非对称方式)。私钥用于创建令牌,公钥用于在客户端级别验证。

找到要提供的密钥你可以给任何东西,“sudsif”,“sdfn2173”,任何长度

您可以使用联机生成器,也可以手动写入

我更喜欢使用openssl

C: \Users\xyz\Desktop>openssl rand-base64 1265JymYzDDqqLW8Eg

生成,然后使用基数64进行编码

C: \Users\xyz\Desktop>openssl rand-out openssl-secret.txt-hex 20

生成的值保存在名为“openssl-secret.txt”的文件中

生成并存储到文件中。

一件事是给定12将生成,仅12个字符,但由于它是以64为基数编码的,因此它将是(4/3*n)上限值。

我建议阅读这篇文章

https://auth0.com/blog/brute-forcing-hs256-is-possible-the-importance-of-using-strong密钥到签名-jwts/

2
  • 4
    你可以跑openssl rand<。。。args>|clip将其复制到剪贴板而不是写入文件 评论 2021年1月26日23:01
  • 2
    使用OpenSSL的CSPRNG是一个好主意,但12字节有点短!此外,Auth0的博客帖子也不太好。问题不仅在于密钥的长度,还在于熵。因此,必须采用CSPRNG。较长的密码是不够的。rfc-editor.org/rfc/rfc7515#第10.1节 评论 2023年9月4日10:34
16

对于JWT,签名算法HMAC SHA256型通常使用。对于该算法,32字节就足够了.

32字节数组可以表示为64个字符长的十六进制字符串,因为2个十六进制字符等于1个字节。使用此终端命令可以生成32个随机字节,表示为十六进制字符串:

$openssl rand-十六进制32

输出内容示例:

第4页第4页第6页第465600页第7895页第865693300页

通过使用上述命令,密钥将在您的计算机上本地生成。使用网站生成密码可能很危险,因此最好在本地进行。

根据您使用的语言,接下来的步骤可能会有所不同。但在C#中,可以使用以下函数将十六进制字符串转换为字节数组:

公共静态字节[]FromHexStringToByteArray(字符串hexString){var byteArray=新字节[hexString.Length/2];for(var i=0;i<hexString.Length;i+=2){byteArray[i/2]=转换。ToByte(hexString.Substring(i,2),16);}返回byteArray;}

在C#中,可以这样生成JWT令牌:

公共字符串GenerateJwtToken(){var issuer=“https://auth.example.com";var受众=“https://api.example.com";var expiryInMinutes=(int)TimeSpan。起始日期(1)。总分钟数;var secretKey=“4f1feeca525de4cdb064656007da3edac7895a87ff0ea865693300fb8b6e8f9c”;var键=FromHexStringToByteArray(secretKey);var securityKey=新的对称安全密钥(密钥);var credentials=新签名凭据(securityKey,SecurityAlgorithms.HmacSha256);var now=日期时间偏移。UtcNow;var索赔=新[]{新索赔(JwtRegisteredClaimNames.Iss,签发人),新索赔(JwtRegisteredClaimNames.Aud,观众),新索赔(JwtRegisteredClaimNames.Exp,now.AddMinutes(expiryInMinutes))。ToUnixTimeSeconds()。ToString()),新索赔(JwtRegisteredClaimNames.Nbf,现在是.ToUnixTimeSeconds()。ToString()),new Claim(JwtRegisteredClaimNames.Iat,现在.ToUnixTimeSeconds()。ToString()),新声明(JwtRegisteredClaimNames.Jti,Guid.NewGuid()。ToString()),new Claim(JwtRegisteredClaimNames.Sub,“123”),//用户ID};var令牌=新的JwtSecurityToken(发行人:发行人,观众:观众,索赔:索赔,到期时间:现在。UtcDateTime公司。添加分钟(expiryInMinutes),签名凭据:凭据);var tokenHandler=新的JwtSecurityTokenHanler();return tokenHandler。WriteToken(令牌);}

在上面的示例中,键是十六进制字符串的形式,并被转换为字节数组。接下来是的一个实例签名凭据创建时将密钥和算法设置为HMAC SHA256。稍后,将使用凭据创建令牌。

13

要生成64字节的唯一密钥,请在节点中运行以下命令:

crypto.randomBytes(64).toString(“十六进制”);
8

如果您正在查找JWT_AUTH_secret_key的密钥,则可以使用此处生成的任何密钥:

https://api.wordpress.org/secret-key/1.1/salt/

这通常用于“WP REST API的JWT身份验证”(https://wordpress.org/plugins/jwt-authentication-for-wp-rest-api/)

也许你是像我一样来这里寻找答案的人:D

2

我使用这个在我的fastAPI服务器上实现了基于JWT的身份验证辅导的,我遇到了这个问题,所以分享了我的两分钱。

就JWT(JSON Web令牌)而言密钥是一个非常关键的部分,用于签名和验证令牌。

什么是密钥?

基于JWT的身份验证密钥用于令牌签名过程。这对JWT安全性非常重要,因为它有助于检查在传输过程中令牌是否已被修改。秘密通过一种特殊算法(例如HMAC SHA256)应用,产生的JWT的一部分就是它的签名。签名用于检查发送回服务器的令牌是否真实且未被更改。

如何生成密钥

密钥还必须是一个随机的、高熵的字符串,并且必须保持安全和机密。它必须足够长且随机,以抵抗暴力和猜测攻击。以下是获得安全密钥的方法:

方法1:使用OpenSSL

以下是如何在OpenSSL的帮助下生成密钥,OpenSSL是一个强大的实用程序,您也可以使用它执行加密操作。例如,可以生成256位的密钥:

openssl rand-base64 32

这将创建32个字节的随机数据,我们可以在Base64编码中用作私钥。

方法2:编程语言

Node.js示例

const crypto=require('crypto');const secret=crypto.randomBytes(64).toString('hex');console.log(机密);

此脚本使用内置加密货币Node.js本身中的模块生成一个64字节的十六进制字符串,该字符串可以完美地用作安全的随机密钥。

Python示例

导入机密secret=机密.token_hex(32)打印(机密)

这个秘密Python模块用于创建加密强随机数,这些随机数可用于管理密码、令牌和其他机密等机密。

最佳实践

  • 保守秘密:密钥应保密。如果密钥泄漏,攻击者可以自己颁发合法的令牌。
  • 高熵:对密钥使用长字符组合以达到更高的熵。
  • 不时再生:定期重新设置密钥可以有效地减少可能存在的任何泄漏的影响。
0

应该安全地生成秘密,使攻击者无法猜测和预测它。

您可以查看一下可以生成安全随机数的算法:https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html#secure-随机数生成

我更喜欢用python来创造秘密https://docs.python.org/3/library/secrets.html

以下是如何生成包含至少一个小写字符、至少一个大写字符和至少三个数字的32个字符的字母数字密码:

导入字符串导入机密字母表=字符串.ascii_letters+字符串数字为True时:password=''.join(secrets.choice(字母表)表示范围(32)中的i)if(any(c.islower()for c in password))和any(密码中c的c.isupper())和总和(密码中c的c.isdigit())>=3):打破
0

你的答案

单击“发布您的答案”,表示您同意我们的服务条款并确认您已阅读我们的隐私政策.

不是你想要的答案吗?浏览已标记的其他问题问你自己的问题.