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

firebase/php-jwt

firebase/php-jwt — пакет JWT для PHP с наибольшим числом установок: объект Key фиксирует алгоритм, короткие ключи HMAC отклоняются, но нет ES512 и PS384/512, нет проверок издателя и аудитории, а ES256 принимается и от ключей P-384.
Лицензия
BSD-3-Clause
Проверенная версия
7.2.1 ·
Исходный код
github.com/firebase/php-jwt
Документация
github.com/firebase/php-jwt#readme

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

JOSE и JWT

Алгоритмы JWS
HS256HS384HS512RS256RS384RS512ES256ES384ES256KEdDSA

php-jwt кодирует и декодирует токены функциями JWT::encode() и JWT::decode() поверх расширения openssl и libsodium. Начиная с версии 6 объект Key (ключевой материал плюс алгоритм) обязателен, поэтому алгоритм задаёт ваш код, а не токен: токен RS256 и токен HS256, подделанный с помощью PEM-текста ключа EC, не прошли проверку с new Key($pem, 'ES256'), оба с ошибкой Incorrect key for this algorithm, а неподписанный токен отклоняется с ошибкой Algorithm not supported. Эта защита работает, только пока приложение само создаёт Key: с new Key($pem, 'HS256') подделанный токен прошёл проверку.

Версия 7.2.1 поддерживает HS256/384/512, RS256/384/512, ES256, ES384, ES256K и EdDSA. ES512 не поддерживается (Algorithm not supported), для PS256 нужен установленный пакет phpseclib/phpseclib, а PS384 и PS512 недоступны вовсе: для токенов от издателей, которые используют эти алгоритмы, нужна web-token или lcobucci/jwt. Ключи HMAC короче хеша отклоняются (Provided key is too short).

ES256 не привязан к P-256: токен с заголовком ES256, подписанный ключом P-384, библиотека сама создала и сама же приняла, хотя другие библиотеки такой токен отклоняют. Подписи ES256 преобразуются в 64-байтовый формат raw r‖s, которого требует JWS; подпись DER не проходит проверку с ошибкой Signature verification failed. Проверьте подпись токена в отладчике JWT.

Утверждения обрабатываются минимально. exp, nbf и iat проверяются, только если они есть в токене, JWT::$leeway — глобальное статическое свойство со значением 0 по умолчанию, а проверки издателя и аудитории нет вовсе, поэтому пример проверяет exp и iss сам. Для проверки по JWKS JWK::parseKeySet() превращает набор ключей в объекты Key; набор ключей поставщика можно разобрать в проверке JWKS.

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

Ничего из приведённого ниже не скопировано из документации. 20 октября 2026 г. проверка content/libraries/verify/firebase-php-jwt/run.sh запустила firebase/php-jwt 7.2.1 в образе контейнера php:8.5.11-cli-trixie; содержимое страницы должно совпадать с выводом проверки, а у каждого факта приведены соответствующие строки вывода.

  • Алгоритмы JWS

    HS256HS384HS512RS256RS384RS512ES256ES384ES256KEdDSA

    Подпись и проверка работают для HS256/384/512, RS256/384/512, ES256, ES384, ES256K и EdDSA; ES512 и PS384/512 не поддерживаются, а для PS256 нужен phpseclib.

    Что записала проверка
    fact jws-algorithms = HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384, ES256K, EdDSA
    evidence jws-algorithms: PS256: DomainException: phpseclib/phpseclib is required for PS256 support
    evidence jws-algorithms: PS384: DomainException: Algorithm not supported
    evidence jws-algorithms: PS512: DomainException: Algorithm not supported
    evidence jws-algorithms: ES512: DomainException: Algorithm not supported
    evidence jws-algorithms: Ed25519: no key family
  • Кодировка подписи ES256

    raw

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

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

    rejected

    Подпись ES256 в кодировке DER не проходит проверку с ошибкой SignatureInvalidException: Signature verification failed.

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

    required

    decode() требует объект Key с алгоритмом: просто строка с ключом вызывает ошибку, а токен RS256 при проверке с new Key($ecPem, 'ES256') отклоняется с ошибкой Incorrect key for this algorithm.

    Что записала проверка
    fact jwt-algorithm-allowlist = required
    evidence jwt-algorithm-allowlist: RS256 token against new Key(EC PEM, 'ES256'): UnexpectedValueException: Incorrect key for this algorithm
    evidence jwt-algorithm-allowlist: decode() with a bare key string: UnexpectedValueException: "kid" empty, unable to lookup correct key
  • Неподписанные токены (alg none)

    rejected

    Неподписанный токен отклоняется с ошибкой Algorithm not supported.

    Что записала проверка
    fact jwt-alg-none = rejected
    evidence jwt-alg-none: UnexpectedValueException: Algorithm not supported
  • Открытый ключ в роли секрета HMAC

    rejected

    Токен HS256, подписанный с текстом PEM в качестве ключа, не прошёл проверку с Key для ES256, но с new Key($pem, 'HS256') прошёл.

    Что записала проверка
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: Key(PEM, 'ES256'): UnexpectedValueException: Incorrect key for this algorithm
    evidence jwt-hmac-public-key: Key(PEM, 'HS256') built by the application: ok
  • Сверка кривой с alg

    not-enforced

    Токен ES256, подписанный ключом P-384, был успешно создан и прошёл проверку.

    Что записала проверка
    fact jwt-ecdsa-curve-check = not-enforced
    evidence jwt-ecdsa-curve-check: ok
  • Допустимое расхождение часов по умолчанию

    0s

    JWT::$leeway равно 0: токен, срок действия которого истёк тремя секундами раньше, был отклонён.

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

    no

    Токен без exp успешно декодирован.

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

    rejected

    5-байтовый ключ HS256 отклоняется с ошибкой Provided key is too short.

    Что записала проверка
    fact jwt-hmac-short-secret = rejected
    evidence jwt-hmac-short-secret: 5-byte HS256 key: DomainException: Provided key is too short

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

Выпустите токен ES256, декодируйте его с допуском в 30 секунд и объектом Key, который фиксирует алгоритм, затем явно проверьте exp и iss.
<?php
require 'vendor/autoload.php';

use Firebase\JWT\JWT;
use Firebase\JWT\Key;

$private = openssl_pkey_new(['private_key_type' => OPENSSL_KEYTYPE_EC, 'curve_name' => 'prime256v1']);
$publicPem = openssl_pkey_get_details($private)['key'];

$token = JWT::encode(['sub' => 'user-42', 'iss' => 'https://auth.example.com', 'exp' => time() + 600], $private, 'ES256');

// The Key object pins the algorithm; leeway is a static setting, 0 by default.
JWT::$leeway = 30;
$claims = JWT::decode($token, new Key($publicPem, 'ES256'));

// php-jwt checks exp/nbf/iat only when present and never checks iss or aud: do it here.
if (!isset($claims->exp) || $claims->iss !== 'https://auth.example.com') {
    throw new UnexpectedValueException('unexpected claims');
}
echo 'sub: ', $claims->sub, "\n";

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

Подготовка
curl -fsSL -o /tmp/composer.phar https://getcomposer.org/download/2.10.3/composer.phar && echo '7a2d379d5b8ffdaa028580ef26494c36d2feef4b178d3dd1473a4dbc5e17c8d6 /tmp/composer.phar' | sha256sum -c - >/dev/null && apt-get -qq update >/dev/null && apt-get -qq install -y unzip >/dev/null 2>&1 && COMPOSER_ALLOW_SUPERUSER=1 php /tmp/composer.phar require --quiet firebase/php-jwt:7.2.1
Команда
php usage.php
Вывод должен совпадать (регулярное выражение)
sub: user-42

Библиотеки