Aller au contenu
ecdsa.com
Python

python-jose

python-jose, une bibliothèque JOSE encore courante dans les tutoriels FastAPI : algorithms facultatif, pas de prise en charge de PS ni d’EdDSA, des jetons ES256 signés avec des clés P-384 et un avis de sécurité toujours ouvert qui permet à une clé publique DER de servir de secret HMAC.
Licence
MIT
Version vérifiée
3.5.0 ·
Code source
github.com/mpdavis/python-jose
Documentation
python-jose.readthedocs.io/en/latest/

Compatibilité en un coup d’œil

JOSE et JWT

Algorithmes JWS
HS256HS384HS512RS256RS384RS512ES256ES384ES512

python-jose implémente JWS, JWE, JWK et JWT avec des backends interchangeables (ecdsa en pur Python, pycryptodome ou cryptography) ; les essais ont installé l’extra cryptography. Des années de tutoriels FastAPI et Flask l’ont répandue, mais son développement est lent : la 3.4.0 (février 2025) est arrivée près de quatre ans après la 3.3.0, et les nouveaux projets choisissent plutôt PyJWT, jwcrypto ou joserfc.

algorithms est facultatif dans jwt.decode(), et sans lui l’algorithme est tiré de l’en-tête du jeton. Combiné à l’avis GHSA-3qf3-8w2g-rqmx (CVE-2026-85394), publié en septembre 2026 et non corrigé dans la 3.5.0, cela devient exploitable : le garde-fou qui empêche d’utiliser des clés publiques comme secrets HMAC reconnaît le texte PEM et OpenSSH, mais pas la même clé sous forme d’octets DER. Les essais l’ont reproduit : un jeton HS256 dont la clé HMAC était la clé publique DER a été vérifié avec cette clé quand algorithms était omis, et rejeté avec The specified alg value is not allowed dès que algorithms=["ES256"] était passé. Passez toujours algorithms, conservez les clés publiques sous forme de chaînes PEM et vérifiez vos secrets avec le vérificateur de secrets JWT.

Son jeu d’algorithmes est le plus ancien des bibliothèques Python : uniquement les familles HS, RS et ES. PS256, EdDSA et ES256K lèvent Algorithm … not supported., si bien que les jetons des fournisseurs passés à Ed25519 ou à RSA-PSS ne peuvent pas être vérifiés. ES256 n’est pas non plus lié à P-256 : une clé P-384 a produit, sous un en-tête ES256, une signature de 96 octets que python-jose a vérifiée, mais que d’autres bibliothèques, comme jose et PyJWT, refusent.

Les signatures ES256 font 64 octets en r‖s brut, et une signature DER échoue avec Signature verification failed. ; le débogueur JWT montre quelle forme contient un jeton. exp est facultatif et la marge de tolérance est nulle, sauf si vous définissez require_exp et leeway dans options, comme le fait l’exemple.

Les jetons non signés ont été rejetés avec Unable to find an algorithm for key, même avec algorithms=["none"].

Les faits et les vérifications qui les fondent

Rien de ce qui suit n’est copié d’une documentation. La vérification content/libraries/verify/python-jose/run.sh a exécuté python-jose 3.5.0 sur Python 3.14.8; installed with the [cryptography] extra; EC key class from jose.backends.cryptography_backend dans l’image de conteneur python:3.14.8-slim-trixie le 20 octobre 2026 ; la page doit correspondre à ce que la vérification a affiché, et chaque fait cite les lignes en question.

  • Algorithmes JWS

    HS256HS384HS512RS256RS384RS512ES256ES384ES512

    HS, RS et ES en 256/384/512 bits ; PS256/384/512, ES256K, EdDSA, Ed25519 et Ed448 ont levé JWSError: Algorithm … not supported.

    Relevé par la vérification
    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.
  • Encodage des signatures ES256

    raw

    Les signatures ES256 font 64 octets, en r‖s brut.

    Relevé par la vérification
    fact jws-ecdsa-encoding = raw
  • Signature DER dans un JWS

    rejected

    Une signature ES256 encodée en DER échoue avec JWTError: Signature verification failed.

    Relevé par la vérification
    fact jws-der-signature = rejected
    evidence jws-der-signature: JWTError: Signature verification failed.
  • Liste d’algorithmes autorisés

    from-key

    algorithms est facultatif : un jeton ES256 a été vérifié sans lui, et un jeton RS256 présenté à la clé EC n’a échoué qu’à la vérification de la signature.

    Relevé par la vérification
    fact jwt-algorithm-allowlist = from-key
    evidence jwt-algorithm-allowlist: no algorithms argument: verified; RS256 token against the EC key: JWTError: Signature verification failed.
  • Jetons non signés (alg none)

    rejected

    Les jetons non signés lèvent JWKError: Unable to find an algorithm for key, avec ou sans algorithms=["none"].

    Relevé par la vérification
    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
  • Clé publique utilisée comme secret HMAC

    accepted

    Les clés publiques PEM et OpenSSH sont refusées comme secrets HMAC, mais l’encodage DER de la même clé publique a été accepté : un jeton HS256 dont c’était la clé a été vérifié quand algorithms était omis (avis GHSA-3qf3-8w2g-rqmx, toujours ouvert pour la 3.5.0).

    Relevé par la vérification
    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.
  • Courbe vérifiée par rapport à alg

    not-enforced

    La signature ES256 avec une clé P-384 a réussi et produit une signature de 96 octets que python-jose a lui-même vérifiée.

    Relevé par la vérification
    fact jwt-ecdsa-curve-check = not-enforced
    evidence jwt-ecdsa-curve-check: signing allowed; verification: verified; signature length 96 bytes under an ES256 header
  • Décalage d’horloge toléré par défaut

    0s

    Aucune marge de tolérance par défaut : un jeton expiré depuis trois secondes a été rejeté.

    Relevé par la vérification
    fact jwt-clock-skew = 0s
  • Claim d’expiration obligatoire

    no

    Un jeton sans exp a été décodé avec succès, sauf si require_exp est activé.

    Relevé par la vérification
    fact jwt-exp-required = no

Utilisation

Émettre un jeton ES256 et le décoder avec l’algorithme et l’audience fixés, exp obligatoire et 30 secondes de tolérance.
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"])

Le banc de test des extraits de code exécute chaque exemple tel quel dans l’image de conteneur python:3.14.8-slim-trixie, et l’exécution échoue si la sortie ne correspond pas.

Mise en place
pip install --quiet --root-user-action=ignore 'python-jose[cryptography]==3.5.0' cryptography==50.0.2
Commande
python usage.py
La sortie doit correspondre à cette expression régulière
sub: user-42

Bibliothèques