跳到主要内容
ecdsa.com
Python

python-jose

python-jose,至今仍常见于 FastAPI 教程的 JOSE 库:algorithms 是可选的,不支持 PS 和 EdDSA,ES256 令牌可以用 P-384 密钥签名,还有一个尚未修复的安全公告:DER 公钥可以充当 HMAC 密钥。
许可证
MIT
检查的版本
3.5.0 ·
源代码
github.com/mpdavis/python-jose
文档
python-jose.readthedocs.io/en/latest/

兼容性概览

JOSE 与 JWT

JWS 算法
HS256HS384HS512RS256RS384RS512ES256ES384ES512

python-jose 通过可插拔的后端——纯 Python 的 ecdsa、pycryptodome 或 cryptography——实现 JWS、JWE、JWK 和 JWT;本次检查安装的是 cryptography 可选依赖。多年来 FastAPI 和 Flask 教程的推荐让它广为流行,但它的开发进展缓慢:3.4.0(2025年2月)距 3.3.0 将近四年,新项目通常改用 PyJWT、jwcrypto 或 joserfc。

jwt.decode() 中的 algorithms 是可选的,不传时算法取自令牌头部。结合2026年9月发布、在 3.5.0 中仍未修复的安全公告 GHSA-3qf3-8w2g-rqmx(CVE-2026-85394),这一点可被利用:防止把公钥用作 HMAC 密钥的检查能识别 PEM 和 OpenSSH 文本,却识别不出以 DER 字节形式提供的同一个密钥。本次检查复现了这个问题——以 DER 公钥为 HMAC 密钥的 HS256 令牌,在省略 algorithms 时用该公钥验证通过;而一旦传入 algorithms=["ES256"],就会被拒绝,报错 The specified alg value is not allowed。务必传入 algorithms;公钥要以 PEM 字符串形式保存;并用 JWT 密钥检测工具检查密钥。

它的算法集是 Python 库中最老旧的:只有 HS、RS 和 ES 系列。PS256、EdDSA 和 ES256K 会抛出 Algorithm … not supported.,因此已改用 Ed25519 或 RSA-PSS 的提供商所签发的令牌无法验证。ES256 也没有与 P-256 绑定:P-384 密钥在 ES256 头部下生成了 96 字节的签名,python-jose 能验证通过,但 jose 和 PyJWT 等其他库会拒绝。

ES256 签名是 64 字节的原始 r‖s,DER 签名会报错 Signature verification failed.;JWT 调试器会显示令牌采用的是哪种形式。除非像示例那样在 options 中设置 require_exp 和 leeway,否则 exp 是可选的,容差为零。

未签名的令牌被拒绝,报错 Unable to find an algorithm for key,即使传入 algorithms=["none"] 也一样。

事实及其依据的检查

以下内容均非抄自文档。检查脚本 content/libraries/verify/python-jose/run.sh 于2026年10月20日在容器镜像 python:3.14.8-slim-trixie 中运行了 python-jose 3.5.0(运行时为 Python 3.14.8; installed with the [cryptography] extra; EC key class from jose.backends.cryptography_backend);本页内容必须与它的输出一致,每条事实都列出了对应的输出行。

  • JWS 算法

    HS256HS384HS512RS256RS384RS512ES256ES384ES512

    支持 256/384/512 位的 HS、RS 和 ES;PS256/384/512、ES256K、EdDSA、Ed25519 和 Ed448 抛出 JWSError: Algorithm … not supported.

    检查记录
    fact jws-algorithms = HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384, ES512
    evidence jws-algorithms: PS256: JWSError: Algorithm PS256 not supported.
    evidence jws-algorithms: PS384: JWSError: Algorithm PS384 not supported.
    evidence jws-algorithms: PS512: JWSError: Algorithm PS512 not supported.
    evidence jws-algorithms: ES256K: JWSError: Algorithm ES256K not supported.
    evidence jws-algorithms: EdDSA: JWSError: Algorithm EdDSA not supported.
    evidence jws-algorithms: Ed25519: JWSError: Algorithm Ed25519 not supported.
    evidence jws-algorithms: Ed448: JWSError: Algorithm Ed448 not supported.
  • ES256 签名编码

    raw

    ES256 签名是 64 字节的原始 r‖s。

    检查记录
    fact jws-ecdsa-encoding = raw
  • JWS 中的 DER 签名

    rejected

    DER 编码的 ES256 签名会报错 JWTError: Signature verification failed.

    检查记录
    fact jws-der-signature = rejected
    evidence jws-der-signature: JWTError: Signature verification failed.
  • 算法允许列表

    from-key

    algorithms 是可选的:不传它时 ES256 令牌验证通过,而用 EC 密钥验证 RS256 令牌时,直到签名验证这一步才失败。

    检查记录
    fact jwt-algorithm-allowlist = from-key
    evidence jwt-algorithm-allowlist: no algorithms argument: verified; RS256 token against the EC key: JWTError: Signature verification failed.
  • 未签名的令牌(alg none)

    rejected

    无论是否传入 algorithms=["none"],未签名的令牌都会抛出 JWKError: Unable to find an algorithm for key。

    检查记录
    fact jwt-alg-none = rejected
    evidence jwt-alg-none: with the EC key: JWKError: Unable to find an algorithm for key
    evidence jwt-alg-none: algorithms ['none']: JWKError: Unable to find an algorithm for key
  • 把公钥用作 HMAC 密钥

    accepted

    PEM 和 OpenSSH 公钥不能用作 HMAC 密钥,但同一公钥的 DER 编码被接受了:省略 algorithms 时,以它为密钥的 HS256 令牌验证通过(安全公告 GHSA-3qf3-8w2g-rqmx,在 3.5.0 中仍未修复)。

    检查记录
    fact jwt-hmac-public-key = accepted
    evidence jwt-hmac-public-key: DER public key bytes, no algorithms: verified
    evidence jwt-hmac-public-key: DER public key bytes, algorithms ['ES256']: JWTError: The specified alg value is not allowed
    evidence jwt-hmac-public-key: PEM public key, algorithms ['HS256']: JWKError: The specified key is an asymmetric key or x509 certificate and should not be used as an HMAC secret.
    evidence jwt-hmac-public-key: PEM public key, no algorithms: JWKError: The specified key is an asymmetric key or x509 certificate and should not be used as an HMAC secret.
    evidence jwt-hmac-public-key: OpenSSH public key, algorithms ['HS256']: JWKError: The specified key is an asymmetric key or x509 certificate and should not be used as an HMAC secret.
  • 按 alg 检查曲线

    not-enforced

    用 P-384 密钥签名 ES256 成功,生成了 96 字节的签名,python-jose 自身也验证通过。

    检查记录
    fact jwt-ecdsa-curve-check = not-enforced
    evidence jwt-ecdsa-curve-check: signing allowed; verification: verified; signature length 96 bytes under an ES256 header
  • 默认时钟偏差

    0s

    默认没有容差:三秒前过期的令牌被拒绝。

    检查记录
    fact jwt-clock-skew = 0s
  • 是否要求过期时间声明

    no

    除非设置了 require_exp,否则不含 exp 的令牌能成功解码。

    检查记录
    fact jwt-exp-required = no

用法

签发 ES256 令牌,然后在锁定算法和受众、要求 exp 并留出 30 秒容差的条件下解码。
import time

from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec
from jose import jwt

key = ec.generate_private_key(ec.SECP256R1())
private_pem = key.private_bytes(serialization.Encoding.PEM, serialization.PrivateFormat.PKCS8, serialization.NoEncryption()).decode()
public_pem = key.public_key().public_bytes(serialization.Encoding.PEM, serialization.PublicFormat.SubjectPublicKeyInfo).decode()

token = jwt.encode({"sub": "user-42", "aud": "api", "exp": int(time.time()) + 600}, private_pem, algorithm="ES256")

# Always pass algorithms: without it python-jose takes the algorithm from the token header.
claims = jwt.decode(token, public_pem, algorithms=["ES256"], audience="api", options={"require_exp": True, "leeway": 30})
print("sub:", claims["sub"])

代码片段测试工具会在容器镜像 python:3.14.8-slim-trixie 中原样运行每个示例,只要输出不匹配,运行就会失败。

环境准备
pip install --quiet --root-user-action=ignore 'python-jose[cryptography]==3.5.0' cryptography==50.0.2
命令
python usage.py
输出必须匹配(正则表达式)
sub: user-42

库