Zum Inhalt springen
ecdsa.com
Python

python-jose

python-jose, eine JOSE-Bibliothek, die in FastAPI-Tutorials noch verbreitet ist: optionales algorithms, keine Unterstützung für PS oder EdDSA, mit P-384-Schlüsseln signierte ES256-Tokens und ein offener Sicherheitshinweis, nach dem ein öffentlicher Schlüssel in DER als HMAC-Secret durchgeht.
Lizenz
MIT
Geprüfte Version
3.5.0 ·
Quellcode
github.com/mpdavis/python-jose
Dokumentation
python-jose.readthedocs.io/en/latest/

Kompatibilität auf einen Blick

JOSE und JWT

JWS-Algorithmen
HS256HS384HS512RS256RS384RS512ES256ES384ES512

python-jose implementiert JWS, JWE, JWK und JWT mit austauschbaren Backends – ecdsa in reinem Python, pycryptodome oder cryptography; für die Prüfung wurde das Extra cryptography installiert. Durch jahrelange FastAPI- und Flask-Tutorials ist die Bibliothek weit verbreitet, doch die Entwicklung ist langsam: 3.4.0 (Februar 2025) erschien fast vier Jahre nach 3.3.0, und neue Projekte wählen stattdessen meist PyJWT, jwcrypto oder joserfc.

algorithms ist in jwt.decode() optional, und ohne diese Angabe stammt der Algorithmus aus dem Header des Tokens. Zusammen mit dem Sicherheitshinweis GHSA-3qf3-8w2g-rqmx (CVE-2026-85394), veröffentlicht im September 2026 und in 3.5.0 nicht behoben, lässt sich das ausnutzen: Der Schutz davor, öffentliche Schlüssel als HMAC-Secrets zu verwenden, erkennt PEM- und OpenSSH-Text, aber nicht denselben Schlüssel als DER-Bytes. Die Prüfung reproduzierte das: Ein HS256-Token, dessen HMAC-Schlüssel der öffentliche Schlüssel in DER war, bestand die Prüfung gegen diesen Schlüssel, wenn algorithms fehlte, und wurde mit The specified alg value is not allowed abgelehnt, sobald algorithms=["ES256"] übergeben wurde. Übergib immer algorithms, verwende öffentliche Schlüssel als PEM-Strings und prüfe Secrets mit dem JWT-Secret-Checker.

Die Algorithmusauswahl ist die älteste unter den Python-Bibliotheken: nur die Familien HS, RS und ES. PS256, EdDSA und ES256K lösen Algorithm … not supported. aus, sodass sich Tokens von Anbietern, die auf Ed25519 oder RSA-PSS umgestiegen sind, nicht prüfen lassen. ES256 ist auch nicht an P-256 gebunden: Ein P-384-Schlüssel erzeugte unter einem Header ES256 eine 96-Byte-Signatur, die bei python-jose die Prüfung bestand, die andere Bibliotheken wie jose und PyJWT aber ablehnen.

ES256-Signaturen bestehen aus 64 Byte rohem r‖s, und eine DER-Signatur scheitert mit Signature verification failed.; der JWT-Debugger zeigt, welche Form ein Token enthält. exp ist optional und die Toleranz (Leeway) null, sofern du nicht require_exp und leeway in options setzt, wie es das Beispiel tut.

Unsignierte Tokens wurden mit Unable to find an algorithm for key abgelehnt, selbst mit algorithms=["none"].

Fakten und die Prüfungen dahinter

Nichts im Folgenden ist aus einer Dokumentation abgeschrieben. Das Prüfskript content/libraries/verify/python-jose/run.sh hat python-jose 3.5.0 unter Python 3.14.8; installed with the [cryptography] extra; EC key class from jose.backends.cryptography_backend am 20. Oktober 2026 im Container-Image python:3.14.8-slim-trixie ausgeführt; die Seite muss mit seiner Ausgabe übereinstimmen, und jeder Fakt führt die zugehörigen Zeilen auf.

  • JWS-Algorithmen

    HS256HS384HS512RS256RS384RS512ES256ES384ES512

    HS, RS und ES bei 256/384/512 Bit; PS256/384/512, ES256K, EdDSA, Ed25519 und Ed448 lösten JWSError: Algorithm … not supported. aus.

    Von der Prüfung aufgezeichnet
    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.
  • Kodierung von ES256-Signaturen

    raw

    ES256-Signaturen bestehen aus 64 Byte rohem r‖s.

    Von der Prüfung aufgezeichnet
    fact jws-ecdsa-encoding = raw
  • DER-Signatur in einer JWS

    rejected

    Eine DER-kodierte ES256-Signatur scheitert mit JWTError: Signature verification failed.

    Von der Prüfung aufgezeichnet
    fact jws-der-signature = rejected
    evidence jws-der-signature: JWTError: Signature verification failed.
  • Allowlist der Algorithmen

    from-key

    algorithms ist optional: Ein ES256-Token bestand die Prüfung ohne diese Angabe, und ein RS256-Token scheiterte am EC-Schlüssel erst bei der Signaturprüfung.

    Von der Prüfung aufgezeichnet
    fact jwt-algorithm-allowlist = from-key
    evidence jwt-algorithm-allowlist: no algorithms argument: verified; RS256 token against the EC key: JWTError: Signature verification failed.
  • Unsignierte Tokens (alg none)

    rejected

    Unsignierte Tokens lösen JWKError: Unable to find an algorithm for key aus, mit oder ohne algorithms=["none"].

    Von der Prüfung aufgezeichnet
    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
  • Öffentlicher Schlüssel als HMAC-Secret

    accepted

    Öffentliche Schlüssel in PEM und OpenSSH werden als HMAC-Secrets abgelehnt, die DER-Kodierung desselben öffentlichen Schlüssels wurde dagegen akzeptiert: Ein HS256-Token mit diesem Wert als Schlüssel bestand die Prüfung, wenn algorithms fehlte (Sicherheitshinweis GHSA-3qf3-8w2g-rqmx, für 3.5.0 offen).

    Von der Prüfung aufgezeichnet
    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.
  • Abgleich der Kurve mit alg

    not-enforced

    Das Signieren mit ES256 und einem P-384-Schlüssel gelang und erzeugte eine 96-Byte-Signatur, die bei python-jose selbst die Prüfung bestand.

    Von der Prüfung aufgezeichnet
    fact jwt-ecdsa-curve-check = not-enforced
    evidence jwt-ecdsa-curve-check: signing allowed; verification: verified; signature length 96 bytes under an ES256 header
  • Standardtoleranz für Uhrabweichungen

    0s

    Standardmäßig keine Toleranz (Leeway): Ein Token, das drei Sekunden zuvor abgelaufen war, wurde abgelehnt.

    Von der Prüfung aufgezeichnet
    fact jwt-clock-skew = 0s
  • Ablauf-Claim erforderlich

    no

    Ein Token ohne exp ließ sich erfolgreich dekodieren, sofern require_exp nicht gesetzt ist.

    Von der Prüfung aufgezeichnet
    fact jwt-exp-required = no

Verwendung

Stelle ein ES256-Token aus und dekodiere es, wobei Algorithmus und Audience festgelegt sind, exp verlangt wird und 30 Sekunden Toleranz gelten.
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"])

Die Snippet-Testumgebung führt jedes Beispiel unverändert im Container-Image python:3.14.8-slim-trixie aus, und der Lauf schlägt fehl, wenn die Ausgabe nicht übereinstimmt.

Einrichtung
pip install --quiet --root-user-action=ignore 'python-jose[cryptography]==3.5.0' cryptography==50.0.2
Befehl
python usage.py
Ausgabe muss passen (regulärer Ausdruck)
sub: user-42

Bibliotheken