Перейти к содержимому
ecdsa.com
Python

PyJWT

PyJWT от José Padilla — самая используемая библиотека JWT для Python: явный список algorithms обязателен, alg: none никогда не проходит проверку, открытые ключи не принимаются как секреты HMAC — но срок действия необязателен, а допуск на расхождение часов равен нулю.
Лицензия
MIT
Проверенная версия
2.15.1 ·
Исходный код
github.com/jpadilla/pyjwt
Документация
pyjwt.readthedocs.io/en/stable/

Совместимость вкратце

JOSE и JWT

Алгоритмы JWS
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512ES256KEdDSA

PyJWT кодирует и декодирует токены JSON Web Token с помощью jwt.encode() и jwt.decode(), а ключи подписи получает с эндпоинта JWKS через PyJWKClient. Асимметричные алгоритмы даёт набор опциональных зависимостей crypto, который подтягивает pyca/cryptography; с ним версия 2.15.1 поддерживала семейства HS, RS, PS и ES, ES256K и EdDSA, но не более новые имена Ed25519 и Ed448, для которых выбрасывалось NotImplementedError: Algorithm not supported.

Проверка по умолчанию строгая. jwt.decode() без algorithms завершается ошибкой It is required that you pass in a value for the "algorithms" argument, неподписанный токен с alg: none ни разу не прошёл проверку — даже с algorithms=["none"], — а открытый ключ PEM или OpenSSH, переданный как секрет HS256, отклонён с ошибкой InvalidKeyError. ES256 привязан к P-256: подпись ключом P-384 завершилась ошибкой несовпадения кривой.

Что библиотека оставляет на вас: exp необязателен, поэтому токен без него декодируется бессрочно, если не указать это утверждение в списке require аргумента options, а leeway по умолчанию равен нулю, поэтому серверы, у которых часы расходятся на несколько секунд, уже по-разному определяют, истёк ли срок действия токена. Короткие секреты HMAC принимаются лишь с предупреждением InsecureKeyLengthWarning, которое легко пропустить в логах, — проверьте секрет инструментом проверки секретов JWT.

Подписи ES256 — это 64-байтовый формат raw r‖s, которого требует JWS; токен, подпись которого собрана в DER (например, при подписи через KMS), не проходит проверку с общей ошибкой Signature verification failed. Вставьте токен в отладчик JWT, чтобы увидеть длину подписи, и преобразуйте её в конвертере DER ⇄ raw.

Пример фиксирует алгоритм, издателя и аудиторию, требует exp и допускает расхождение часов в 30 секунд — это четыре настройки, с которыми jwt.decode() можно безопасно вызывать для токенов из другого сервиса.

Факты и проверки, на которых они основаны

Ничего из приведённого ниже не скопировано из документации. 20 октября 2026 г. проверка content/libraries/verify/pyjwt/run.sh запустила PyJWT 2.15.1 в среде cryptography 50.0.2 в образе контейнера python:3.14.8-slim-trixie; содержимое страницы должно совпадать с выводом проверки, а у каждого факта приведены соответствующие строки вывода.

  • Алгоритмы JWS

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512ES256KEdDSA

    HS, RS, PS и ES на 256/384/512 бит, ES256K и EdDSA: подпись и проверка прошли успешно; Ed25519 и Ed448 вызвали NotImplementedError: Algorithm not supported.

    Что записала проверка
    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
  • Кодировка подписи ES256

    raw

    Подписи ES256 — 64 байта в формате raw r‖s.

    Что записала проверка
    fact jws-ecdsa-encoding = raw
  • Подпись в DER внутри JWS

    rejected

    Проверка токена с подписью ES256 в кодировке DER завершается ошибкой InvalidSignatureError: Signature verification failed.

    Что записала проверка
    fact jws-der-signature = rejected
    evidence jws-der-signature: InvalidSignatureError: Signature verification failed
  • Список разрешённых алгоритмов

    required

    jwt.decode() без algorithms выбрасывает DecodeError: It is required that you pass in a value for the "algorithms" argument when calling decode().

    Что записала проверка
    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().
  • Неподписанные токены (alg none)

    rejected

    Неподписанный токен отклоняется с ошибкой The specified alg value is not allowed при проверке ключом ES256 и с ошибкой Signature verification failed даже при algorithms=["none"]; прочитать его можно только с verify_signature: False.

    Что записала проверка
    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
  • Открытый ключ в роли секрета HMAC

    rejected

    Открытый ключ PEM или OpenSSH, переданный как секрет HS256, вызывает InvalidKeyError: The specified key is an asymmetric key or x509 certificate and should not be used as an HMAC secret.

    Что записала проверка
    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.
  • Сверка кривой с alg

    enforced

    При подписи ES256 ключом P-384 выбрасывается The key's curve 'secp384r1' does not match the expected curve 'secp256r1' for this algorithm.

    Что записала проверка
    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
  • Допустимое расхождение часов по умолчанию

    0s

    По умолчанию расхождение часов не допускается: токен, срок действия которого истёк 3 секунды назад, отклонён.

    Что записала проверка
    fact jwt-clock-skew = 0s
  • Обязательное утверждение о сроке действия

    no

    Токен без exp успешно декодирован; чтобы требовать это утверждение, укажите exp в списке require аргумента options.

    Что записала проверка
    fact jwt-exp-required = no
  • Короткий секрет HMAC

    accepted-with-warning

    Секрет HMAC длиной 5 байт подошёл и для подписи, и для проверки, а единственным сигналом было InsecureKeyLengthWarning.

    Что записала проверка
    fact jwt-hmac-short-secret = accepted-with-warning
    evidence jwt-hmac-short-secret: 5-byte HMAC secret: accepted; warnings: InsecureKeyLengthWarning

Использование

Выпустите токен ES256 и декодируйте его с фиксированными алгоритмом, издателем и аудиторией, обязательным exp и допуском на расхождение часов в 30 секунд.
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"])

Тестовый стенд для фрагментов кода запускает каждый пример без изменений в образе контейнера python:3.14.8-slim-trixie, и запуск завершается ошибкой, если вывод не совпадает.

Подготовка
pip install --quiet --root-user-action=ignore 'PyJWT[crypto]==2.15.1' cryptography==50.0.2
Команда
python usage.py
Вывод должен совпадать (регулярное выражение)
sub: user-42

Библиотеки