JWT 如何工作并且安全吗?
JWT 代表 JSON Web 令牌
通用定义称,它是一种开放的行业标准 RFC 7519 方法,用于在双方之间安全地表达声明
所以让我们把它分解成更简单的逻辑,以便理解它的实用性和工作方式!
JWT 是由微软的一些开发人员构建的,他们最初是为了信息交换而构建的,后来它被重新用于授权。
在安全流程中,身份验证不仅验证用户的身份,还授予用户访问资源的权限。JWT
是一种无状态会话,因此它不需要像 Cookie 那样保存在服务器端的数据库中,它只存在于客户端。
JWT 由以下部分组成:
标头. 有效载荷. 签名
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Header 是关于 token 的元数据,它是
const base64Url = require("base64-url")
// used for Base64 and URL Encoding Decoding
const header = base64Url.encode(
JSON.stringify({
alg :"HS256", // algorithm : none, HS256, RS256, PS256 etc ..
type :"JWT",
...
})
);
//outputs : eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9
请注意,它没有加密,它只是编码,这意味着您可以使用 base64 解码,您将获得清晰的 JSON 对象。
有效载荷包含我们想要发送的消息以及有关令牌本身的不同信息
const base64Url = require("base64-url")
// used for Base64 and URL Encoding Decoding
const header = base64Url.encode(
JSON.stringify({
sub:"1234567890", //subject
iss:"Darken", //issuer
aud:"My API", //audience used for auth as well
exp:1633895355, //expiration datetime
iat:1633895235, //issued at datetime
...
})
);
//outputs : eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9
//lIiwiaWF0IjoxNTE2MjM5MDIyfQ
再次强调,它并非加密,只是编码而已,这意味着您可以使用 Base64 解码,然后获得明文的 JSON 对象。
目前为止,我们还没有对信息进行安全保护,所以您可能会想,这如何保证安全?所有这些中的身份验证又在哪里?
而这正是签名发挥作用的地方!
签名是某个函数使用头部、有效载荷、密钥和哈希函数的结果。
密钥是最重要的部分,建议使用 256 位密钥,并且不要硬编码(将其保存在 process.env 中)。
请注意,如果我们使用非对称加密,则在计算签名时,算法会同时使用两个密钥(私钥和公钥)。
因此签名通常是这样计算的:
const crypto = require("crypto") // cryptography library
const base64Url = require("base64-url")
const secret = process.env.SECRET
//Again ! please use a 256bit secret key
const content = "${header}.${payload}"
//used for Base64 and URL Encoding Decoding
const signature = base64Url.escape(
crypto.createHmac('sha256',secret)
.update(content)
.digest('base64')
);
//outputs : SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
现在,这会创建一个 HMAC 加密(基于哈希的消息认证码),这是一种将密钥和哈希组合成黑客无法解开的混合体的加密技术。
身份验证部分就在这里!这条消息的内容被篡改了吗?
请记住,令牌等于:
const token = "${header}.${payload}.${signature}"
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwib
mFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fw
pMeJf36POk6yJV_adQssw5c
由于黑客可以更改签名,但无法猜测正确的签名(他不知道密钥),因此当攻击者更改有效载荷或标头时,签名将不再与数据匹配。
因此,假设黑客解码了有效载荷并将其更改为:
{
"sub": "This was changed",
"name": "AchrafAffes",
"iat": 1516239022
}
//The payload encoded will then be changed to :
eyJzdWIiOiJUaGlzIHdhcyBjaGFuZ2VkIiwibmFtZSI6IkFjaHJhZkFmZmVzIiwiaW
F0IjoxNTE2MjM5MDIyfQ
再说一遍!由于黑客无法猜出新编码有效载荷的正确签名(没有密钥),那么当服务器解码标头和有效载荷并重新计算新签名时,它将是:do3cSS2wLRUM6cmqVqvFZVpCwJkeO0BieF0h0oTWaBE,
除非黑客知道密钥(记住使用单个对称密钥时要使用 256 位密钥),否则黑客不可能猜到,此时服务器将预测有效载荷或标头已被更改,因此它将忽略该请求。
现在您已经了解了 JWT 的工作原理,那么我们如何在实际中使用它呢?
对我来说,我按如下方式使用它,用户登录,服务器检查凭据以确定该用户是否存在,如果存在,服务器会生成一个令牌并将其发送给用户(服务器不保存副本)然后用户将令牌保存在其本地存储中(令牌应该有一个较短的到期日期时间,因为它容易受到 XSS 攻击,我将在以后的另一篇文章中解释)
每当用户想要访问某些内容时,它都会在其标头中发送令牌,然后服务器对其进行验证,如果经过验证,则服务器响应,否则服务器将以 403 Forbidden 错误响应。
在其他一些解决方案中,我们实现了一个身份验证服务器(AS),用户首先通过 AS,然后重定向到资源服务器(API),资源服务器将对每个请求验证令牌。
如果您使用 nodeJs,则可以使用 jsonwebtoken 包,轻松实现 JWT
var jwt = require('jsonwebtoken');
const secret = 'secretkey'
//please make sure to use a 265bit key
const data= {username:"achraf",other:"stuffHere"}
//to generate the data we use
let token = jwt.sign(
data,
secret,
{expiresIn : '2 min'} //other options can be used
);
//and to verify it you can use
jwt.verify(token,secret, function(err, tokendata){
if(err){
console.log("Unauthorized request")
}
if(tokendata){
console.log("verified")
}
})
因此,让我们快速讨论一下可以使用的最推荐的算法:
HS256 :HMAC + SHA-265
(依赖于共享对称密钥)RS256 / RSASSA + SHA-256
(依赖于私钥/公钥 RSA 密钥对,但可能会导致网络过载并使用更多 CPU 进行计算)ES256:使用 p-265 和 SHA-265 的 ECDSA
(依赖于私钥/公钥 RSA 密钥对,但更短)
我以后会尝试详细讨论这些算法
最后我想谈谈 cookies 和 JWT 之间的区别:
鏂囩珷鏉ユ簮锛�https://dev.to/darken/jwt-how-does-it-work-and-is-it-secure-37n
- Cookies 需要存储在服务器端,而 JWT 是无状态的
- 由于 cookies 需要数据库,因此每次客户端请求时都会查询该数据库
- cookies 容易受到 CSRF 和 XSS 攻击,而 JWT 仅容易受到 XSS 攻击。