跳到主要内容
ecdsa.com
Python

PyJWT

José Padilla 开发的 PyJWT,Python 中使用最广的 JWT 库:必须显式提供 algorithms 列表,alg: none 永远无法验证通过,公钥不能用作 HMAC 密钥——但过期时间是可选的,容差为零。
许可证
MIT
检查的版本
2.15.1 ·
源代码
github.com/jpadilla/pyjwt
文档
pyjwt.readthedocs.io/en/stable/

兼容性概览

JOSE 与 JWT

JWS 算法
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512ES256KEdDSA

PyJWT 用 jwt.encode() 和 jwt.decode() 编码和解码 JSON Web 令牌,并用 PyJWKClient 从 JWKS 端点获取签名密钥。非对称算法由可选依赖 crypto 提供,它会引入 pyca/cryptography;装上它后,2.15.1 支持 HS、RS、PS 和 ES 系列以及 ES256K 和 EdDSA,但不支持较新的 Ed25519 和 Ed448 名称,使用它们会抛出 NotImplementedError: Algorithm not supported。

它的验证默认设置很严格。不传 algorithms 调用 jwt.decode() 会报错 It is required that you pass in a value for the "algorithms" argument;未签名的 alg: none 令牌从未验证通过——即使传入 algorithms=["none"] 也不行;用作 HS256 密钥的 PEM 或 OpenSSH 公钥被拒绝,报错 InvalidKeyError。ES256 与 P-256 绑定:用 P-384 密钥签名时因曲线不匹配而失败。

留给你处理的部分:exp 是可选的,因此除非在 options 参数的 require 中列出它,否则不含它的令牌永远都能解码;leeway 默认为零,因此时钟相差几秒的服务器就会对是否过期产生分歧。过短的 HMAC 密钥也会被接受,只给出一条 InsecureKeyLengthWarning,在日志中很容易被忽略——可以用 JWT 密钥检测工具测试密钥。

ES256 签名是 JWS 要求的 64 字节原始 r‖s 形式;签名被拼装成 DER 的令牌——例如来自 KMS 签名调用——会报出笼统的 Signature verification failed。把令牌粘贴到 JWT 调试器中查看签名长度,再用 DER ⇄ 原始格式转换器转换。

示例锁定了算法、颁发者和受众,要求 exp,并允许 30 秒的时钟漂移——有了这四项设置,才能放心地对其他服务签发的令牌调用 jwt.decode()。

事实及其依据的检查

以下内容均非抄自文档。检查脚本 content/libraries/verify/pyjwt/run.sh 于2026年10月20日在容器镜像 python:3.14.8-slim-trixie 中运行了 PyJWT 2.15.1(运行时为 cryptography 50.0.2);本页内容必须与它的输出一致,每条事实都列出了对应的输出行。

  • JWS 算法

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512ES256KEdDSA

    256/384/512 位的 HS、RS、PS 和 ES,以及 ES256K 和 EdDSA,签名与验证均成功;Ed25519 和 Ed448 抛出 NotImplementedError: Algorithm not supported。

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

    raw

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

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

    rejected

    带 DER 编码 ES256 签名的令牌会报错 InvalidSignatureError: Signature verification failed。

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

    required

    不传 algorithms 调用 jwt.decode() 会抛出 DecodeError: It is required that you pass in a value for the "algorithms" argument when calling decode().

    检查记录
    fact jwt-algorithm-allowlist = required
    evidence jwt-algorithm-allowlist: DecodeError: It is required that you pass in a value for the "algorithms" argument when calling decode().
  • 未签名的令牌(alg none)

    rejected

    未签名的令牌在用 ES256 密钥验证时报错 The specified alg value is not allowed,即使设置了 algorithms=["none"] 也会报错 Signature verification failed;只有 verify_signature: False 能读取它。

    检查记录
    fact jwt-alg-none = rejected
    evidence jwt-alg-none: with an ES256 key: InvalidAlgorithmError: The specified alg value is not allowed
    evidence jwt-alg-none: algorithms ['none'] and no key: InvalidSignatureError: Signature verification failed
    evidence jwt-alg-none: verify_signature False: returned claims
  • 把公钥用作 HMAC 密钥

    rejected

    把 PEM 或 OpenSSH 公钥用作 HS256 密钥时,会抛出 InvalidKeyError: The specified key is an asymmetric key or x509 certificate and should not be used as an HMAC secret.

    检查记录
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: PEM public key as HS256 secret: InvalidKeyError: 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 as HS256 secret: InvalidKeyError: The specified key is an asymmetric key or x509 certificate and should not be used as an HMAC secret.
  • 按 alg 检查曲线

    enforced

    用 P-384 密钥签名 ES256 时抛出 The key's curve 'secp384r1' does not match the expected curve 'secp256r1' for this algorithm。

    检查记录
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: InvalidKeyError: The key's curve 'secp384r1' does not match the expected curve 'secp256r1' for this algorithm
  • 默认时钟偏差

    0s

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

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

    no

    不含 exp 的令牌解码成功;在 options 参数的 require 中列出 exp,即可将其设为必需。

    检查记录
    fact jwt-exp-required = no
  • 过短的 HMAC 密钥

    accepted-with-warning

    5 字节的 HMAC 密钥可以正常签名和验证,唯一的提示是一条 InsecureKeyLengthWarning。

    检查记录
    fact jwt-hmac-short-secret = accepted-with-warning
    evidence jwt-hmac-short-secret: 5-byte HMAC secret: accepted; warnings: InsecureKeyLengthWarning

用法

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

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

key = ec.generate_private_key(ec.SECP256R1())
now = datetime.datetime.now(tz=datetime.timezone.utc)

token = jwt.encode(
    {"sub": "user-42", "iss": "https://auth.example.com", "aud": "api", "exp": now + datetime.timedelta(minutes=10)},
    key,
    algorithm="ES256",
)

# algorithms is mandatory; also require exp and allow a little clock drift.
claims = jwt.decode(
    token,
    key.public_key(),
    algorithms=["ES256"],
    audience="api",
    issuer="https://auth.example.com",
    leeway=30,
    options={"require": ["exp", "iss", "aud"]},
)
print("sub:", claims["sub"])

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

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

库