Pular para o conteúdo
ecdsa.com
Python

python-jose

O python-jose, uma biblioteca JOSE ainda comum em tutoriais de FastAPI: algorithms opcional, sem suporte a PS nem a EdDSA, tokens ES256 assinados com chaves P-384 e um alerta de segurança em aberto que permite que uma chave pública DER funcione como segredo HMAC.
Licença
MIT
Versão verificada
3.5.0 ·
Código-fonte
github.com/mpdavis/python-jose
Documentação
python-jose.readthedocs.io/en/latest/

Visão geral da compatibilidade

JOSE e JWT

Algoritmos JWS
HS256HS384HS512RS256RS384RS512ES256ES384ES512

O python-jose implementa JWS, JWE, JWK e JWT com backends intercambiáveis — o ecdsa em Python puro, o pycryptodome ou o cryptography; o teste instalou o extra cryptography. Anos de tutoriais de FastAPI e Flask o tornaram muito difundido, mas o desenvolvimento é lento: a 3.4.0 (fevereiro de 2025) saiu quase quatro anos depois da 3.3.0, e projetos novos costumam escolher o PyJWT, o jwcrypto ou o joserfc.

Em jwt.decode(), algorithms é opcional e, sem ele, o algoritmo vem do cabeçalho do token. Somado ao alerta GHSA-3qf3-8w2g-rqmx (CVE-2026-85394), publicado em setembro de 2026 e sem correção na 3.5.0, isso é explorável: a proteção contra o uso de chaves públicas como segredos HMAC reconhece texto PEM e OpenSSH, mas não a mesma chave como bytes DER. O teste reproduziu o problema — um token HS256 cuja chave HMAC era a chave pública DER foi verificado com essa chave quando algorithms foi omitido e foi rejeitado com The specified alg value is not allowed assim que se passou algorithms=["ES256"]. Sempre passe algorithms, mantenha as chaves públicas como strings PEM e verifique segredos com o verificador de segredos JWT.

O conjunto de algoritmos é o mais antigo entre as bibliotecas Python: apenas as famílias HS, RS e ES. PS256, EdDSA e ES256K lançam Algorithm … not supported., então tokens de provedores que migraram para Ed25519 ou RSA-PSS não podem ser verificados. O ES256 também não está vinculado à P-256: uma chave P-384 produziu, sob um cabeçalho ES256, uma assinatura de 96 bytes que o python-jose verificou, mas que outras bibliotecas, como o jose e o PyJWT, recusam.

As assinaturas ES256 são 64 bytes de r‖s brutos, e uma assinatura DER falha com Signature verification failed.; o depurador de JWT mostra qual forma um token traz. exp é opcional e a tolerância é zero, a menos que você defina require_exp e leeway em options, como faz o exemplo.

Tokens sem assinatura foram rejeitados com Unable to find an algorithm for key, mesmo com algorithms=["none"].

Os fatos e as verificações por trás deles

Nada abaixo foi copiado da documentação. A verificação content/libraries/verify/python-jose/run.sh executou python-jose 3.5.0 em Python 3.14.8; installed with the [cryptography] extra; EC key class from jose.backends.cryptography_backend na imagem de contêiner python:3.14.8-slim-trixie em 20 de outubro de 2026; a página precisa corresponder ao que a verificação imprimiu, e cada fato lista essas linhas.

  • Algoritmos JWS

    HS256HS384HS512RS256RS384RS512ES256ES384ES512

    HS, RS e ES em 256/384/512 bits; PS256/384/512, ES256K, EdDSA, Ed25519 e Ed448 lançaram JWSError: Algorithm … not supported.

    Registrado pela verificação
    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.
  • Codificação da assinatura ES256

    raw

    As assinaturas ES256 são 64 bytes de r‖s brutos.

    Registrado pela verificação
    fact jws-ecdsa-encoding = raw
  • Assinatura DER dentro de um JWS

    rejected

    Uma assinatura ES256 codificada em DER falha com JWTError: Signature verification failed.

    Registrado pela verificação
    fact jws-der-signature = rejected
    evidence jws-der-signature: JWTError: Signature verification failed.
  • Lista de algoritmos permitidos

    from-key

    algorithms é opcional: um token ES256 foi verificado sem ele, e um token RS256 verificado com a chave EC só falhou na verificação da assinatura.

    Registrado pela verificação
    fact jwt-algorithm-allowlist = from-key
    evidence jwt-algorithm-allowlist: no algorithms argument: verified; RS256 token against the EC key: JWTError: Signature verification failed.
  • Tokens sem assinatura (alg none)

    rejected

    Tokens sem assinatura lançam JWKError: Unable to find an algorithm for key, com ou sem algorithms=["none"].

    Registrado pela verificação
    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
  • Chave pública usada como segredo HMAC

    accepted

    Chaves públicas PEM e OpenSSH são recusadas como segredos HMAC, mas a codificação DER da mesma chave pública foi aceita: um token HS256 que a usava como chave foi verificado quando algorithms foi omitido (alerta GHSA-3qf3-8w2g-rqmx, em aberto na 3.5.0).

    Registrado pela verificação
    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.
  • Curva conferida com o alg

    not-enforced

    Assinar ES256 com uma chave P-384 funcionou e produziu uma assinatura de 96 bytes que o próprio python-jose verificou.

    Registrado pela verificação
    fact jwt-ecdsa-curve-check = not-enforced
    evidence jwt-ecdsa-curve-check: signing allowed; verification: verified; signature length 96 bytes under an ES256 header
  • Tolerância padrão de diferença de relógio

    0s

    Sem tolerância por padrão: um token que tinha expirado três segundos antes foi rejeitado.

    Registrado pela verificação
    fact jwt-clock-skew = 0s
  • Claim de expiração obrigatória

    no

    Um token sem exp foi decodificado com sucesso, a menos que require_exp esteja definido.

    Registrado pela verificação
    fact jwt-exp-required = no

Uso

Emita um token ES256 e decodifique-o com o algoritmo e o público-alvo fixados, exp obrigatória e 30 segundos de tolerância.
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"])

O executor de trechos de código roda cada exemplo literalmente na imagem de contêiner python:3.14.8-slim-trixie, e a execução falha se a saída não corresponder.

Configuração
pip install --quiet --root-user-action=ignore 'python-jose[cryptography]==3.5.0' cryptography==50.0.2
Comando
python usage.py
A saída deve corresponder a (expressão regular)
sub: user-42

Bibliotecas