JWT:给你满满的安全感

166 阅读2分钟

不只是字符串拼接

header.payload.signature:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoiMDAxIiwidXNlcm5hbWUiOiJhZG1pbiJ9LCJpYXQiOjE3NTMyNTYxOTcsImV4cCI6MTc1MzI1NjI4M30.9FQwcZLyJSyrOj6vJkYho8uFIDgCU23C2B5pvEoMTjM
  • Header:指定算法与类型(如 HS256 + JWT),决定签名生成方式
  • Payload:携带业务数据(如用户ID)和标准声明(iat签发时间/exp过期时间)
  • Signature:防伪核心,通过密钥对前两部分加密生成唯一指纹

Header/Payload 仅做 Base64Url 编码(非加密),敏感数据需额外处理!

header、payload、signature转译:

image.png

Signature 签名

signature:算法+密钥对编码后的header和payload进行签名,对结果进行base64编码的结果

const crypto = require('crypto');

// 1. 准备原始数据
const header = Buffer.from(JSON.stringify({ alg: 'HS256', typ: 'JWT' })).toString('base64url');
const payload = Buffer.from(JSON.stringify({ user: { id: '001' } })).toString('base64url');

// 2. 生成签名核心操作
const signature = crypto.createHmac('sha256', '你的密钥')
    .update(`${header}.${payload}`)
    .digest('base64url');

通过 HMAC-SHA256 将密钥与编码后的头部、载荷绑定,任何字符修改都会导致签名失效

本质:HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)


验证过程:

const jwt = require('jsonwebtoken');

// 验证场景
const fakeToken = 'eyJhbG...改动的部分.MTIzNDU2'; 

try {
    const decoded = jwt.verify(fakeToken, '你的密钥');
    console.log('验证通过');
} catch (err) {
    console.log('攻击拦截!原因:', err.message); 
    // 典型错误:invalid signature / expired token
}

验证机制

  1. 分离 Token 的三段数据
  2. 用相同算法+密钥重新计算签名
  3. 比对计算结果与传入的 Signature 是否完全一致

安全陷阱

  1. 密钥管理

    • 弱密钥(如 "secret")可直接暴力破解
    • 解决方案:使用环境变量存储,定期轮换
  2. 算法欺骗攻击

    • 篡改 Header 中的 algnone 绕过验证
    • 防御:代码中显式指定算法 jwt.verify(token, key, { algorithms: ['HS256'] })
  3. 数据泄露风险

    • Payload 内容可被 Base64 解码直接查看
    // 攻击者控制台操作
    atob('eyJ1c2VyIjp7ImlkIjoiMDAxIiwidXNlcm5hbWUiOiJhZG1pbiJ9'); 
    // 输出:{"user":{"id":"001","username":"admin"}}
    
    • 敏感数据(如密码)必须加密后存储

实践

// 安全增强版实现
const token = jwt.sign(
    {
        user: { id: '001' },
        // 自动注入标准声明
    }, 
    process.env.JWT_SECRET, 
    {
        algorithm: 'HS256',
        expiresIn: '2h',  // 强制过期时间
        header: { kid: '密钥ID' } // 支持密钥轮换
    }
);

// 解密时验证声明有效性
jwt.verify(token, getKeyByKid(kid), {
    algorithms: ['HS256'],
    clockTolerance: 30, // 允许30秒时钟偏差
    ignoreExpiration: false // 强制检查过期
});

工程化要点

  • 密钥与业务分离,使用 KMS 或 Vault 托管
  • 携带 kid(Key ID)支持动态密钥更新
  • 短期令牌 + Refresh Token 双机制平衡安全与体验

JWT 是防篡改票据而非加密容器,该用 AES 加密的数据别放进 Payload!