Pular para o conteúdo
ecdsa.com
Python

PyJWT

O PyJWT, de José Padilla, a biblioteca JWT mais usada em Python: uma lista algorithms explícita é obrigatória, alg: none nunca passa na verificação e chaves públicas são recusadas como segredos HMAC — mas a expiração é opcional e a tolerância é zero.
Licença
MIT
Versão verificada
2.15.1 ·
Código-fonte
github.com/jpadilla/pyjwt
Documentação
pyjwt.readthedocs.io/en/stable/

Visão geral da compatibilidade

JOSE e JWT

Algoritmos JWS
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512ES256KEdDSA

O PyJWT codifica e decodifica JSON Web Tokens com jwt.encode() e jwt.decode() e busca chaves de assinatura em um endpoint JWKS com PyJWKClient. Os algoritmos assimétricos vêm do extra crypto, que instala o pyca/cryptography; com ele, a 2.15.1 tratou as famílias HS, RS, PS e ES, o ES256K e o EdDSA, mas não os nomes mais novos Ed25519 e Ed448, que lançaram NotImplementedError: Algorithm not supported.

Seus padrões de verificação são estritos. jwt.decode() sem algorithms falha com It is required that you pass in a value for the "algorithms" argument, um token alg: none sem assinatura nunca passou na verificação — nem mesmo com algorithms=["none"] — e uma chave pública PEM ou OpenSSH usada como segredo HS256 foi recusada com InvalidKeyError. O ES256 está vinculado à P-256: assinar com uma chave P-384 falhou com um erro de curva incompatível.

O que fica por sua conta: exp é opcional, então um token sem ela é decodificado para sempre, a menos que você a inclua em require no argumento options, e leeway é zero por padrão, então servidores com poucos segundos de diferença de relógio já discordam sobre a expiração. Segredos HMAC curtos são aceitos apenas com um InsecureKeyLengthWarning, fácil de passar despercebido nos logs — teste um segredo com o verificador de segredos JWT.

As assinaturas ES256 estão na forma r‖s bruta de 64 bytes que o JWS exige; um token cuja assinatura foi montada em DER — por exemplo, a partir de uma chamada de assinatura a um KMS — falha com o genérico Signature verification failed. Cole o token no depurador de JWT para ver o tamanho da assinatura e converta com o conversor DER ⇄ bruto.

O exemplo fixa o algoritmo, o emissor e o público-alvo, exige exp e admite 30 segundos de diferença de relógio — as quatro configurações que tornam seguro chamar jwt.decode() com tokens de outro serviço.

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

Nada abaixo foi copiado da documentação. A verificação content/libraries/verify/pyjwt/run.sh executou PyJWT 2.15.1 em cryptography 50.0.2 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

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512ES256KEdDSA

    Assinatura e verificação funcionaram com HS, RS, PS e ES em 256/384/512 bits, ES256K e EdDSA; Ed25519 e Ed448 lançaram NotImplementedError: Algorithm not supported.

    Registrado pela verificação
    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
  • 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

    Um token com uma assinatura ES256 codificada em DER falha com InvalidSignatureError: Signature verification failed.

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

    required

    jwt.decode() sem algorithms lança DecodeError: It is required that you pass in a value for the "algorithms" argument when calling decode().

    Registrado pela verificação
    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().
  • Tokens sem assinatura (alg none)

    rejected

    Um token sem assinatura é recusado com The specified alg value is not allowed contra uma chave ES256 e com Signature verification failed mesmo com algorithms=["none"]; só verify_signature: False permite lê-lo.

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

    rejected

    Uma chave pública PEM ou OpenSSH usada como segredo HS256 lança InvalidKeyError: The specified key is an asymmetric key or x509 certificate and should not be used as an HMAC secret.

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

    enforced

    Assinar ES256 com uma chave P-384 lança The key's curve 'secp384r1' does not match the expected curve 'secp256r1' for this algorithm.

    Registrado pela verificação
    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
  • 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; para exigi-la, inclua exp em require no argumento options.

    Registrado pela verificação
    fact jwt-exp-required = no
  • Segredo HMAC curto

    accepted-with-warning

    Um segredo HMAC de 5 bytes funcionou na assinatura e na verificação, e o único sinal foi um InsecureKeyLengthWarning.

    Registrado pela verificação
    fact jwt-hmac-short-secret = accepted-with-warning
    evidence jwt-hmac-short-secret: 5-byte HMAC secret: accepted; warnings: InsecureKeyLengthWarning

Uso

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

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 'PyJWT[crypto]==2.15.1' cryptography==50.0.2
Comando
python usage.py
A saída deve corresponder a (expressão regular)
sub: user-42

Bibliotecas