Saltar al contenido
ecdsa.com
PHP

firebase/php-jwt

firebase/php-jwt, el paquete JWT más instalado para PHP: un objeto Key que fija el algoritmo y rechazo de las claves HMAC cortas, pero sin ES512 ni PS384/512, sin comprobaciones de emisor ni de audiencia, y con ES256 aceptado incluso con claves P-384.
Licencia
BSD-3-Clause
Versión comprobada
7.2.1 ·
Código fuente
github.com/firebase/php-jwt
Documentación
github.com/firebase/php-jwt#readme

Compatibilidad de un vistazo

JOSE y JWT

Algoritmos JWS
HS256HS384HS512RS256RS384RS512ES256ES384ES256KEdDSA

php-jwt codifica y decodifica tokens con JWT::encode() y JWT::decode() sobre la extensión openssl y libsodium. Desde la versión 6 es obligatorio un objeto Key —material de clave más algoritmo—, de modo que el algoritmo sale de tu código, no del token: un token RS256 y un token HS256 falsificado con el texto PEM de la clave EC fallaron ambos contra new Key($pem, 'ES256') con Incorrect key for this algorithm, y un token sin firmar falla con Algorithm not supported. La protección solo se mantiene mientras sea la aplicación quien construya el objeto Key; new Key($pem, 'HS256') verificó el token falsificado.

La versión 7.2.1 admite HS256/384/512, RS256/384/512, ES256, ES384, ES256K y EdDSA. ES512 no se admite (Algorithm not supported), PS256 necesita tener instalado phpseclib/phpseclib, y PS384 y PS512 no están disponibles en absoluto: los tokens de emisores que usan esos algoritmos necesitan web-token o lcobucci/jwt. Las claves HMAC más cortas que el hash se rechazan (Provided key is too short).

ES256 no está ligado a P-256: la propia biblioteca produjo y aceptó un token firmado con una clave P-384 bajo un encabezado ES256, aunque otras bibliotecas lo rechazan. Las firmas ES256 se convierten al r‖s en bruto de 64 bytes que exige JWS; una firma DER falla con Signature verification failed. Comprueba la firma de un token en el decodificador de JWT.

El manejo de los claims es mínimo. exp, nbf e iat solo se comprueban si están presentes, JWT::$leeway es una variable estática global con valor predeterminado 0, y no hay ninguna validación de emisor ni de audiencia: el ejemplo comprueba exp e iss por su cuenta. Para la verificación basada en JWKS, JWK::parseKeySet() convierte un conjunto de claves en objetos Key; inspecciona el conjunto de un proveedor con el comprobador de JWKS.

Datos y las comprobaciones que los respaldan

Nada de lo que sigue está copiado de la documentación. La comprobación content/libraries/verify/firebase-php-jwt/run.sh ejecutó firebase/php-jwt 7.2.1 en la imagen de contenedor php:8.5.11-cli-trixie el 20 de octubre de 2026; la página debe coincidir con lo que imprimió, y cada dato cita esas líneas.

  • Algoritmos JWS

    HS256HS384HS512RS256RS384RS512ES256ES384ES256KEdDSA

    Con HS256/384/512, RS256/384/512, ES256, ES384, ES256K y EdDSA se firmó y se verificó; ES512 y PS384/512 no se admiten, y PS256 requiere phpseclib.

    Registrado por la comprobación
    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
  • Codificación de la firma ES256

    raw

    Las firmas ES256 son 64 bytes de r‖s en bruto.

    Registrado por la comprobación
    fact jws-ecdsa-encoding = raw
  • Firma DER dentro de un JWS

    rejected

    Una firma ES256 codificada en DER falla con SignatureInvalidException: Signature verification failed.

    Registrado por la comprobación
    fact jws-der-signature = rejected
    evidence jws-der-signature: Firebase\JWT\SignatureInvalidException: Signature verification failed
  • Lista de algoritmos permitidos

    required

    decode() exige un objeto Key con un algoritmo; una cadena de clave sin más falla, y un token RS256 contra new Key($ecPem, 'ES256') falla con Incorrect key for this algorithm.

    Registrado por la comprobación
    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
  • Tokens sin firma (alg none)

    rejected

    Un token sin firmar falla con Algorithm not supported.

    Registrado por la comprobación
    fact jwt-alg-none = rejected
    evidence jwt-alg-none: UnexpectedValueException: Algorithm not supported
  • Clave pública usada como secreto HMAC

    rejected

    Un token HS256 con el texto PEM como clave falló contra el objeto Key ES256, pero new Key($pem, 'HS256') lo verificó.

    Registrado por la comprobación
    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
  • Curva cotejada con alg

    not-enforced

    Se produjo y se verificó correctamente un token firmado bajo ES256 con una clave P-384.

    Registrado por la comprobación
    fact jwt-ecdsa-curve-check = not-enforced
    evidence jwt-ecdsa-curve-check: ok
  • Desfase de reloj predeterminado

    0s

    JWT::$leeway es 0: se rechazó un token que había caducado tres segundos antes.

    Registrado por la comprobación
    fact jwt-clock-skew = 0s
    evidence jwt-clock-skew: JWT::$leeway = 0
  • Claim de caducidad obligatorio

    no

    Un token sin exp se decodificó correctamente.

    Registrado por la comprobación
    fact jwt-exp-required = no
  • Secreto HMAC corto

    rejected

    Una clave HS256 de 5 bytes falla con Provided key is too short.

    Registrado por la comprobación
    fact jwt-hmac-short-secret = rejected
    evidence jwt-hmac-short-secret: 5-byte HS256 key: DomainException: Provided key is too short

Uso

Emite un token ES256, decodifícalo con un objeto Key que fija el algoritmo y 30 segundos de tolerancia, y comprueba exp e iss explícitamente.
<?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";

El arnés de pruebas de fragmentos de código ejecuta cada ejemplo tal cual en la imagen de contenedor php:8.5.11-cli-trixie, y la ejecución falla si la salida no coincide.

Configuración
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
Comando
php usage.php
La salida debe coincidir (expresión regular)
sub: user-42

Bibliotecas