本文へスキップ
ecdsa.com
Python

python-jose

python-jose は、今も FastAPI のチュートリアルでよく使われる JOSE ライブラリです。algorithms は省略可能で、PS と EdDSA には対応せず、P-384 鍵で署名した ES256 トークンを作れてしまいます。さらに、DER の公開鍵を HMAC シークレットとして使えてしまう未修正のアドバイザリがあります。
ライセンス
MIT
確認したバージョン
3.5.0 ·
ソースコード
github.com/mpdavis/python-jose
ドキュメント
python-jose.readthedocs.io/en/latest/

互換性の概要

JOSE と JWT

JWS アルゴリズム
HS256HS384HS512RS256RS384RS512ES256ES384ES512

python-jose は JWS、JWE、JWK、JWT を実装しており、バックエンドを差し替えられます(純粋な Python 実装の ecdsa、pycryptodome、cryptography)。チェックでは cryptography の extra をインストールしました。長年の FastAPI や Flask のチュートリアルによって広く普及しましたが、開発のペースは遅く、3.4.0(2025年2月)は 3.3.0 から 4 年近く後のリリースでした。新しいプロジェクトでは通常、代わりに 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 は省略可能で、時刻のずれの許容(leeway)はゼロです。

署名のないトークンは、algorithms=["none"] を指定しても Unable to find an algorithm for key で拒否されました。

事実とその根拠となるチェック

以下の内容はドキュメントからの転記ではありません。2026年10月20日に、チェック content/libraries/verify/python-jose/run.sh がコンテナイメージ 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

    HS、RS、ES の 256/384/512 ビットに対応します。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 エンコーディングは受け入れられました。これを鍵とした HS256 トークンは、algorithms を省略すると検証を通りました(アドバイザリ 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

    デフォルトでは時刻のずれを許容しません。3 秒前に期限切れになったトークンは拒否されました。

    チェックによる記録
    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

ライブラリ