惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

B
Blog
V
Vulnerabilities – Threatpost
Apple Machine Learning Research
Apple Machine Learning Research
V
V2EX
博客园 - 叶小钗
阮一峰的网络日志
阮一峰的网络日志
人人都是产品经理
人人都是产品经理
Latest news
Latest news
博客园 - 三生石上(FineUI控件)
美团技术团队
aimingoo的专栏
aimingoo的专栏
Google Online Security Blog
Google Online Security Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
T
Threatpost
Y
Y Combinator Blog
T
Tailwind CSS Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
A
Arctic Wolf
C
Cyber Attacks, Cyber Crime and Cyber Security
小众软件
小众软件
Recent Commits to openclaw:main
Recent Commits to openclaw:main
T
Tenable Blog
W
WeLiveSecurity
L
LINUX DO - 热门话题
D
Docker
Cyberwarzone
Cyberwarzone
量子位
A
About on SuperTechFans
The Last Watchdog
The Last Watchdog
雷峰网
雷峰网
C
CERT Recently Published Vulnerability Notes
P
Palo Alto Networks Blog
The Hacker News
The Hacker News
Blog — PlanetScale
Blog — PlanetScale
P
Proofpoint News Feed
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
F
Full Disclosure
The Cloudflare Blog
T
The Blog of Author Tim Ferriss
T
The Exploit Database - CXSecurity.com
Engineering at Meta
Engineering at Meta
O
OpenAI News
Hacker News - Newest:
Hacker News - Newest: "LLM"
Scott Helme
Scott Helme
IT之家
IT之家
S
Secure Thoughts
MongoDB | Blog
MongoDB | Blog
L
Lohrmann on Cybersecurity
博客园 - 司徒正美
Google DeepMind News
Google DeepMind News

东东's Blog

Memos: MacOS 下编译安装 Aseprite 脚本 日本关西系列|Day 2 京都的半日闲逛 美国Apple官网购买礼品卡订阅 ChatGPT Plus 北京・中关村森林公园(2026) 日本关西系列|Day 1 抵达临空城与大阪首日 Memos: 查询 Apple ID 注册时间 Memos: Ghostty 开箱即用配置 代码考古:用 gitcharts 挖掘 Git 仓库的演变轨迹 烹饪日记:香煎罗非鱼 Memos: 新时代程序员的顶级焦虑 Memos: 博客新增图文布局和轮播图效果支持 Memos: 最适合空气炸锅烤着吃的红薯品类 Memos: 记录「95分」好吃的‘小帅香菇面’ 妻子爷爷的‘朝鲜军功奖章’ Memos: 博客切换为 Shiki 代码高亮方案 Memos: 博客新增划线、重点及荧光笔效果支持 记录博客字体分包与字体子集化 Memos: 邻座吃饭的一家三口 人在囧途之哈囧 哈尔滨・乡村的冬季 查看香烟生产日期 哈尔滨・东北虎林园 Memos: 来自日本的 ndjp 提供免费的三级子域名 Memos: 刚听说 autojump, 真的好用 Memos: 体验 OpenCode + Superpowers + GPT 5.2 开发需求 Memos: 杰我睿爆雷 GoReleaser 自动发布 Go 镜像到 DockerHub & GitHub Release 初识 Volta & Corepack 前端版本管理工具 部署 Beszel 把 “小鸡们” 归拢起来 Memos: Claude Code in Action 中文版教程 2025 年度回顾 Memos: 体验 tanaos-text-anonymizer-v1 NER 模型 Memos: 查询 Google 账号注册时间 Memos: 关于 Z30 在室内摄像被手机降维打击这点儿事儿 阅读《我与地坛》 Memos: Ghostty + Neovim + LazyVim Memos: 找到 Cursor 运行巨慢的一个原因 Memos: 京东家政 哈尔滨灵活就业人员医保退休待遇申领条件 记地暖不热的维修过程 Memos: 赛博菩萨 Cloudflare 又挂了 AnyTLS 软件的配置与使用 阅读《在巴东》 Memos: Web Archive 暂时离线 忆时光:十五年前我的家(动迁前夕) macOS 系统部署 Valkey 集群模式 阅读《一个名叫欧维的男人决定去死》 Memos: Cursor 服务故障部分功能不可用 阅读《丰乳肥臀》 爱人回家送奶奶 Memos: AWS 美东可用区 P0 故障(us-east-1) 2025 北京社保下限上调|个体户缴费随之上涨 铁锅重生记 不锈钢盆与放心水源改造计划 阅读《不被大风吹倒》 基于 Supabase 构建示例应用(中篇):实现 Vue 前端页面 基于 Supabase 构建示例应用(上篇):数据库与接口 阅读《三体》之地球往事 Oracle Free 实例重装系统 非京籍个体户缴纳社保(补充):“无有效的汇总预处理信息” 解决办法 阅读《芯片简史》 阅读《简约至上:交互式设计四策略(第2版)》 阅读《审判》 阅读《统计数字会撒谎》 达达秒送骑士 日本关西系列|在动物园前站找到海南本线 观影《长安的荔枝》 Memos: 记录两个在线工具 地球 Online:外卖骑手体验报告 杜师傅夜话:附身与归途 日本关西系列|使用投放硬币的行李寄存箱 日本关西系列|将多余零钱充值到西瓜卡 日本关西系列|网上购买大阪往返白滨高速巴士 乌鲁木齐・赛里木湖 “778 老哥” 摄影摘选(转载) 使用 Restic 来备份重要数据 Backing Up Important Data with Restic Sauvegarder des données importantes avec Restic Resticで重要なデータをバックアップする Cursor 开发 Obsidian 插件记录 非京籍个体户缴纳社保(十):新增并缴纳个人所得税-工资薪金 非京籍个体户缴纳社保(九):公积金开户增员与缴费 非京籍个体户缴纳社保(八):税务申报与工商年报 非京籍个体户缴纳社保(七):社保费用申报与缴纳 非京籍个体户缴纳社保(六):医保公共服务平台 - 增员确认 非京籍个体户缴纳社保(五):北京电子税务局 - 税务报道 非京籍个体户缴纳社保(四):北京市社会保险网上服务平台 - 增员与社保卡领取 非京籍个体户缴纳社保(三):社会保险网上服务平台 - 单位信息登记 非京籍个体户缴纳社保(二):北京 e 窗通平台提交申请 非京籍个体户缴纳社保(一):概览与先期准备 养老保险零基础入门指南(速通版) 了解北京门诊看病工会“二次报销”互助金 注册 US.KG 免费域名(dpdns.org) 白嫖 Cloudflare R2 + Worker 搭建私有镜像仓库 黑龙江・木兰县属小村落的星空(2024) Nginx 启用 HTTPS/3 优化网站的 SSL Labs 总体评级为 A+(禁用旧协议 & 启用 HSTS) 了解 OCSP Stapling 证书吊销验证机制 山东・烟台中秋两三日(2024) 分享改造后的博客发布流程和访问链路 边缘网络:白嫖 Cloudflare R2 博客图床(DNS 国内外分流)
再思 JWT 的使用场景和算法选择
2024-10-06 · via 东东's Blog

在使用第三方服务的时候,我们总会看到其提供 API Key,这是一种简单的验证用户身份的方法,服务端存储着这个 API Key,可以通过 Key 来确定用户权限、吊销 Key 等

相比于 API Key,JWT 有自己的特点和适用场景,如果你不太了解 JWT,可以阅读阮一峰的:《JSON Web Token 入门教程》

回顾 JWT 特点

自包含

JWT 是一个自包含的 Token,它包含了所有必要的信息,不需要服务端存储 Token 的状态。JWT 的载荷(payload)部分可以包含用户信息、权限信息、有效期等。

无状态

由于 JWT 自包含所有必要的信息,服务端不需要存储 Token 的状态,所以 JWT 非常适合用于无状态的应用架构,减少了服务端的负担。

传输安全

JWT 使用密钥进行签名,可以验证 Token 的合法性和完整性,签名或加密确保了 JWT 在传输过程中不会被篡改。

扩展性

JWT 的载荷部分是一个 JSON 对象,可以包含自定义的声明(claims),这使得 JWT 非常灵活,可以根据具体需求扩展其功能。

注意事项:在 JWT 中,避免包含密码等敏感信息,JWT 可防篡改但内容可见。

不可撤销

JWT 一旦生成,无法单独撤销。如果 JWT 被泄露,攻击者可以滥用 Token,直到 Token 过期,因此,JWT 的有效期通常设置得较短,以减少风险。(撤销需要借助服务器有状态机制)

例举一种 JWT 场景误用

以 HS256 算法为例子,以下是生成和验证的代码示例

import time
import jwt

KEY_SECRET = "abncdsr462*********zk52hav3x86dm"

# 生成 JWT 令牌
def generate_jwt_token(key_secret, algorithm="HS256"):
    payload = {
        "iat": int(time.time())
    }
    token = jwt.encode(payload, key_secret, algorithm=algorithm)
    return token

# 校验 JWT 令牌
def verify_jwt_token(token, key_secret, algorithm="HS256"):
    try:
        payload = jwt.decode(token, key_secret, algorithms=[algorithm])
        return payload
    except jwt.InvalidTokenError:
        return None

# 生成令牌
t = generate_jwt_token(KEY_SECRET)
print(f"生成的 JWT:", t)

# 校验令牌
verified_payload = verify_jwt_token(t, KEY_SECRET)
if verified_payload:
    print(f"JWT 验证成功:", verified_payload)
else:
    print("JWT 验证失败")

正确的 JWT 使用场景,会将 JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3Mjc2NjU2MTR9.ItXRitJXiwcDx7X1PHeilgwAs9vt4xaa1lHNKgC3wak 提供给客户端

客户端调用服务端时将其再传给服务端,服务端可以验证这个 Token 没有被篡改、同时从中获取信息

一个错误的用法是将 HS256 的 KEY_SECRET 提供给外部平台,由外部平台生成 JWT。这种方式相当于将 JWT 机制降级为 API Key,使得 JWT 的安全性从“JWT 丢失则其权限会被滥用” 降级为 “API Key 泄露则攻击者可无限制地伪造 JWT,可以对系统进行任意请求”,这属于比较严重的安全漏洞

所以,当决定采用 JWT,就应尽量遵守其规范,避免 “新奇” 的使用方式

不同 JWT 算法使用示例(Python3)

JWT 支持 HS256(密钥)、RS256(基于 RSA)、ES256(基于椭圆曲线)、PS256(基于 RSA-PSS)等加密算法,数字表示位数,越长越安全,除 256 外还支持 384 和 512

HS256 在上文已提供示例,它比较简单、使用的是密钥字符串,也是最常用的 JWT 加密算法,另外三者基于的公钥密码对

生成 RS256 公钥密码对

# 私钥
$ openssl genpkey -algorithm RSA -out rsa_private_key.pem -pkeyopt rsa_keygen_bits:2048

# 公钥
$ openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem

生成 ES256 公钥密码对

# 私钥
$ openssl ecparam -name prime256v1 -genkey -noout -out ec_private.pem

# 公钥
$ openssl ec -in ec_private.pem -pubout -out ec_public.pem

这里选择的曲线进一步说明,prime256v1 P-256,Go 语言的 x509 包默认支持的椭圆曲线是 P-256 和 P-384,另一种常见的曲线是 secp256k1,它是比特币和其他加密货币中常用的椭圆曲线,Go 语言的 x509 包默认不支持它,Python3 是支持的

生成 PS256 公钥密码对

# 私钥
$ openssl genpkey -algorithm RSA -out ps_private_key.pem -pkeyopt rsa_keygen_bits:2048

# 公钥
$ openssl rsa -in ps_private_key.pem -pubout -out ps_public_key.pem

代码示例

import time
import jwt
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend

# 定义常量
RSA_PRIVATE_KEY_PATH = "./id_rsa"
RSA_PUBLIC_KEY_PATH = "./id_rsa_pem.pub"
EC_PRIVATE_KEY_PATH = "./ec_private.pem"
EC_PUBLIC_KEY_PATH = "./ec_public.pem"
PS_PRIVATE_KEY_PATH = "./ps_private_key.pem"
PS_PUBLIC_KEY_PATH = "./ps_public_key.pem"

# 加载私钥
def load_private_key(private_key_path):
    with open(private_key_path, "rb") as key_file:
        private_key = serialization.load_pem_private_key(
            key_file.read(),
            password=None,
            backend=default_backend()
        )
    return private_key

# 加载公钥
def load_public_key(public_key_path):
    with open(public_key_path, "rb") as key_file:
        public_key = serialization.load_pem_public_key(
            key_file.read(),
            backend=default_backend()
        )
    return public_key

# 生成 JWT 令牌
def generate_jwt_token(private_key, algorithm):
    payload = {
        "iat": int(time.time())  # 使用秒级时间戳
    }
    return jwt.encode(payload, private_key, algorithm=algorithm)

# 验证 JWT
def verify_jwt_token(token, public_key, algorithm):
    try:
        decoded = jwt.decode(token, public_key, algorithms=[algorithm])
        return decoded
    except jwt.InvalidTokenError:
        return None

# 主函数
def __main__():
    algorithms = [
        ("RS256", RSA_PRIVATE_KEY_PATH, RSA_PUBLIC_KEY_PATH),
        ("ES256", EC_PRIVATE_KEY_PATH, EC_PUBLIC_KEY_PATH),
        ("PS256", PS_PRIVATE_KEY_PATH, PS_PUBLIC_KEY_PATH),
    ]

    for algorithm, private_key_path, public_key_path in algorithms:
        # 加载私钥和公钥
        private_key = load_private_key(private_key_path)
        public_key = load_public_key(public_key_path)

        # 生成 JWT
        jwt_token = generate_jwt_token(private_key, algorithm)
        print(f"生成的 {algorithm} JWT:", jwt_token)

        # 验证 JWT
        decoded_payload = verify_jwt_token(jwt_token, public_key, algorithm)
        if decoded_payload:
            print(f"{algorithm} JWT 验证成功:", decoded_payload)
        else:
            print(f"{algorithm} JWT 验证失败")

if __name__ == "__main__":
    __main__()

执行输出

基于椭圆曲线算法的 ES256 生成的签名长度,相较于其它算法可谓之遥遥领先,在 JWT 长度敏感的场景下可以优先选择

另外 PS256 和 RS256 相比较,查阅的资料如下:

PS256 使用 RSASSA-PSS,这是一种概率签名方案,引入了随机性,使得每次签名都是唯一的,因为引入了随机性,PS256 提供了比 RS256 更高的安全性,同样资源消耗也会更高一些

另外从 JWT 的是用场景来看,签发与核验 JWT 都由服务端自己完成,所以不存在跟客户端的兼容性问题,选择算法会更加的灵活

综上:推荐优先使用 ES256 算法,安全性高,同时签名、密钥对都是最小的

线上校验工具 JWT.io

这个调试工具支持的算法广泛,本文中提及的算法生成的 JWT 内容都通过 https://jwt.io/ 进行了二次核对

另外可以在 https://jwt.io/libraries 找到相应语言库支持的算法列表