乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • JWT 基础

    • JWT 概述
    • JWT 结构详解
  • 签名与加密

    • JWS 签名基础
    • RS256与ES256签名
    • JWE 加密
  • 密钥管理

    • JWK 与密钥管理
  • 算法原理

    • 核心算法详解
  • 实战与安全

    • JWT 实际应用
    • JWT 安全攻防

JWS 签名基础

本教程结合 广州飞翔科技 (learnto.cn)的实际场景进行讲解。


什么是 JWS

JWS(JSON Web Signature,JSON Web 签名) 是 JWT 中最有用的单一功能。它通过将简单的数据格式与定义明确的签名算法相结合,使 JWT 成为在客户端和中间方之间安全共享数据的理想格式。

签名的目的是允许一个或多个参与方确认 JWT 的 真实性(authenticity)——即 JWT 中包含的数据未被篡改。需要强调的是: 签名不能阻止他人读取 JWT 内容 ,那是加密(JWE)的职责。

检查 JWT 签名的过程称为 验证(validation) 。当令牌头部和载荷中指定的所有限制条件都满足时,该令牌才被视为有效。


签名 JWT 的结构

签名 JWT 由三个元素组成,通过点号分隔:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
部分内容说明
第一部分eyJhbGciOiJIUzI1Ni...Header 的 Base64URL 编码
第二部分eyJzdWIiOiIxMjM0NTY3...Payload 的 Base64URL 编码
第三部分TJVA95OrM7E2cBab...Signature 的 Base64URL 编码

解码后的头部和载荷:

{ "alg": "HS256", "typ": "JWT" }
{ "sub": "1234567890", "name": "John Doe", "admin": true }

紧凑序列化算法概述

JWS 紧凑序列化(JWS Compact Serialization)的算法流程如下:

对于基于 HMAC 的签名算法:

const encodedHeader = base64(utf8(JSON.stringify(header)));
const encodedPayload = base64(utf8(JSON.stringify(payload)));
const signature = base64(hmac(`${encodedHeader}.${encodedPayload}`, secret, sha256));
const jwt = `${encodedHeader}.${encodedPayload}.${signature}`;

对于公钥签名算法(如 RS256):

const encodedHeader = base64(utf8(JSON.stringify(header)));
const encodedPayload = base64(utf8(JSON.stringify(payload)));
const signature = base64(rsassa(`${encodedHeader}.${encodedPayload}`, privateKey, sha256));
const jwt = `${encodedHeader}.${encodedPayload}.${signature}`;

HMAC 签名原理与 HS256

HMAC 是什么

HMAC(Keyed-Hash Message Authentication Code,密钥散列消息认证码) 是一种将特定载荷与 共享密钥(secret) 结合,使用加密哈希函数生成认证码的算法。只有当生成方和验证方都知道该密钥时,才能验证消息的真实性。

HS256=HMAC+SHA-256 。它是 JWT 最常用的签名算法,也是 JWS 规范要求所有实现必须支持的算法。

SHA-256 的作用

SHA-256 是一种加密哈希函数,接收任意长度的消息并产生固定长度的输出(256 位)。相同的消息总是产生相同的输出。哈希函数的 加密(cryptographic) 特性确保从输出恢复原始消息在数学上不可行,因此它是 单向函数(one-way function) 。消息的微小变化将产生完全不同的输出。

共享密钥 vs 公钥方案

特性HMAC(共享密钥)RSASSA/ECDSA(公钥)
密钥类型单一共享密钥私钥签名 + 公钥验证
验证方能否伪造能(持有相同密钥)不能(仅持有公钥)
适用场景内部服务互信一对多分发、第三方验证
性能快较慢(但差距在毫秒级)

签名与验证的 JavaScript 代码示例

以下示例使用流行的 jsonwebtoken 库。

使用 HS256 签名

import jwt from 'jsonwebtoken';

const payload = {
  sub: "1234567890",
  name: "John Doe",
  admin: true
};

const secret = 'my-secret';
const signed = jwt.sign(payload, secret, {
  algorithm: 'HS256',
  expiresIn: '5s' // 如果省略,令牌将不会过期
});

验证令牌

const decoded = jwt.verify(signed, secret, {
  // 永远不要忘记明确指定此项,以防止签名剥离攻击
  algorithms: ['HS256'],
});

jsonwebtoken 库会根据签名和过期日期检查令牌的有效性。如果在创建令牌后 5 秒再检查,它将被视为无效并抛出异常。


签名剥离攻击(Signature Stripping)简介

签名剥离是针对已签名 JWT 的一种常见攻击:

攻击步骤:

  1. 截获合法的签名 JWT。
  2. 移除签名部分(第三个点号后的内容)。
  3. 修改头部,将 alg 改为 none。
  4. 篡改载荷中的数据(如将普通用户改为管理员)。
  5. 将伪造的 JWT 发送给服务端。

如果服务端使用的 JWT 库 未明确指定允许的算法 ,可能将未签名的 JWT 视为有效,导致严重的安全漏洞。

防御措施 :

  • 始终明确指定 algorithms 参数(如 ['HS256'])。
  • 拒绝接受 alg: none 的 JWT(除非业务确实需要)。
  • 使用经过安全审计的 JWT 库,并保持更新。

飞翔科技实战场景:小崔用 HS256 保护 API 购物车数据

广州飞翔科技 的电商平台上,用户将商品加入购物车时,购物车数据需要频繁读写。后端开发 小崔 决定用 JWT 将购物车状态保存在客户端,减少数据库压力。

小崔设计的购物车 JWT 载荷如下:

{
    "items": [10086, 10087, 10088],
    "iat": 1717996400,
    "exp": 1718000000,
    "uid": "cuicui-backend",
    "version": 1
}

签名密钥存储在服务器环境变量中,只有后端知道。前端 黄俪 只负责将 JWT 存入 Cookie 并在请求时自动携带, 从不验证签名——验证工作全部由后端完成。

架构师 白歌 审查代码时特别强调:

"jwt.verify 一定要写死 algorithms: ['HS256'],唔好俾人搞签名剥离攻击。"

运维 李眉 则在 Nginx 层配置了 HttpOnly 和 Secure 标志,降低 XSS 窃取风险。


小结

  • JWS(JSON Web Signature) 提供验证 JWT 数据真实性的能力,签名不阻止读取,只防止篡改。
  • 签名 JWT 的结构为 header.payload.signature,三部分均使用 Base64URL 编码。
  • HS256=HMAC+SHA-256 ,使用共享密钥签名和验证,是 JWS 规范强制要求支持的算法。
  • 签名与验证在 JavaScript 中非常简单,使用 jsonwebtoken 库即可实现。
  • 签名剥离攻击 通过移除签名并修改 alg 为 none 来伪造令牌,防御关键是 始终明确指定允许的算法 。
  • 在飞翔科技的购物车场景中,HS256 有效保护了客户端状态数据的完整性。

在后续教程中,我们将继续探索 RS256、ES256 等公钥签名算法,以及 JWE 加密机制。

下一页
RS256与ES256签名