Aller au contenu
ecdsa.com
PHP

firebase/php-jwt

firebase/php-jwt, le paquet JWT le plus installé pour PHP : un objet Key qui fixe l’algorithme, des clés HMAC courtes refusées, mais ni ES512 ni PS384/512, aucune vérification de l’émetteur ou de l’audience, et ES256 accepté avec des clés P-384.
Licence
BSD-3-Clause
Version vérifiée
7.2.1 ·
Code source
github.com/firebase/php-jwt
Documentation
github.com/firebase/php-jwt#readme

Compatibilité en un coup d’œil

JOSE et JWT

Algorithmes JWS
HS256HS384HS512RS256RS384RS512ES256ES384ES256KEdDSA

php-jwt encode et décode des jetons avec JWT::encode() et JWT::decode(), en s’appuyant sur l’extension openssl et libsodium. Depuis la version 6, un objet Key (matériel de clé et algorithme) est obligatoire : l’algorithme vient donc de votre code, et non du jeton. Un jeton RS256 et un jeton HS256 forgé avec le texte PEM de la clé EC ont tous deux échoué face à new Key($pem, 'ES256') avec Incorrect key for this algorithm, et un jeton non signé échoue avec Algorithm not supported. Cette protection ne tient que tant que c’est l’application qui construit la Key ; new Key($pem, 'HS256') a vérifié le jeton forgé.

La version 7.2.1 gère HS256/384/512, RS256/384/512, ES256, ES384, ES256K et EdDSA. ES512 n’est pas pris en charge (Algorithm not supported), PS256 nécessite l’installation de phpseclib/phpseclib, et PS384 et PS512 ne sont pas disponibles du tout : les jetons d’émetteurs qui utilisent ces algorithmes nécessitent web-token ou lcobucci/jwt. Les clés HMAC plus courtes que le condensat sont rejetées (Provided key is too short).

ES256 n’est pas lié à P-256 : un jeton signé avec une clé P-384 sous un en-tête ES256 a été produit puis accepté par la bibliothèque elle-même, alors que d’autres bibliothèques le refusent. Les signatures ES256 sont converties dans le r‖s brut de 64 octets qu’exige JWS ; une signature DER échoue avec Signature verification failed. Vérifiez la signature d’un jeton dans le débogueur JWT.

Le traitement des claims est minimal. exp, nbf et iat ne sont vérifiés que s’ils sont présents, JWT::$leeway est une propriété statique globale qui vaut 0 par défaut, et l’émetteur comme l’audience ne sont pas du tout validés : l’exemple vérifie lui-même exp et iss. Pour une vérification fondée sur JWKS, JWK::parseKeySet() transforme un jeu de clés en objets Key ; examinez le jeu de clés d’un fournisseur avec le vérificateur JWKS.

Les faits et les vérifications qui les fondent

Rien de ce qui suit n’est copié d’une documentation. La vérification content/libraries/verify/firebase-php-jwt/run.sh a exécuté firebase/php-jwt 7.2.1 dans l’image de conteneur php:8.5.11-cli-trixie le 20 octobre 2026 ; la page doit correspondre à ce que la vérification a affiché, et chaque fait cite les lignes en question.

  • Algorithmes JWS

    HS256HS384HS512RS256RS384RS512ES256ES384ES256KEdDSA

    Signature et vérification réussies avec HS256/384/512, RS256/384/512, ES256, ES384, ES256K et EdDSA ; ES512 et PS384/512 ne sont pas pris en charge, et PS256 nécessite phpseclib.

    Relevé par la vérification
    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
  • Encodage des signatures ES256

    raw

    Les signatures ES256 font 64 octets, en r‖s brut.

    Relevé par la vérification
    fact jws-ecdsa-encoding = raw
  • Signature DER dans un JWS

    rejected

    Une signature ES256 encodée en DER échoue avec SignatureInvalidException: Signature verification failed.

    Relevé par la vérification
    fact jws-der-signature = rejected
    evidence jws-der-signature: Firebase\JWT\SignatureInvalidException: Signature verification failed
  • Liste d’algorithmes autorisés

    required

    decode() exige un objet Key doté d’un algorithme ; une simple chaîne de clé échoue, et un jeton RS256 face à new Key($ecPem, 'ES256') échoue avec Incorrect key for this algorithm.

    Relevé par la vérification
    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
  • Jetons non signés (alg none)

    rejected

    Un jeton non signé échoue avec Algorithm not supported.

    Relevé par la vérification
    fact jwt-alg-none = rejected
    evidence jwt-alg-none: UnexpectedValueException: Algorithm not supported
  • Clé publique utilisée comme secret HMAC

    rejected

    Un jeton HS256 dont la clé est le texte PEM a échoué face à la Key ES256, mais new Key($pem, 'HS256') l’a vérifié.

    Relevé par la vérification
    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
  • Courbe vérifiée par rapport à alg

    not-enforced

    Un jeton signé en ES256 avec une clé P-384 a été produit et vérifié avec succès.

    Relevé par la vérification
    fact jwt-ecdsa-curve-check = not-enforced
    evidence jwt-ecdsa-curve-check: ok
  • Décalage d’horloge toléré par défaut

    0s

    JWT::$leeway vaut 0 : un jeton expiré trois secondes plus tôt a été rejeté.

    Relevé par la vérification
    fact jwt-clock-skew = 0s
    evidence jwt-clock-skew: JWT::$leeway = 0
  • Claim d’expiration obligatoire

    no

    Un jeton sans exp a été décodé avec succès.

    Relevé par la vérification
    fact jwt-exp-required = no
  • Secret HMAC court

    rejected

    Une clé HS256 de 5 octets échoue avec Provided key is too short.

    Relevé par la vérification
    fact jwt-hmac-short-secret = rejected
    evidence jwt-hmac-short-secret: 5-byte HS256 key: DomainException: Provided key is too short

Utilisation

Émettre un jeton ES256, le décoder avec une Key qui fixe l’algorithme et 30 secondes de tolérance, puis vérifier explicitement exp et 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";

Le banc de test des extraits de code exécute chaque exemple tel quel dans l’image de conteneur php:8.5.11-cli-trixie, et l’exécution échoue si la sortie ne correspond pas.

Mise en place
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
Commande
php usage.php
La sortie doit correspondre à cette expression régulière
sub: user-42

Bibliothèques