Aller au contenu
ecdsa.com
PHP

web-token (JWT Framework)

JWT Framework (web-token) de Florent Morselli, l’implémentation JOSE complète pour PHP et Symfony : un AlgorithmManager qui sert de liste d’autorisation, des vérificateurs de claims distincts, tous les algorithmes de signature de la RFC 7518 plus EdDSA, mais pas ES256K.
Licence
MIT
Version vérifiée
4.2.3 ·
Code source
github.com/web-token/jwt-framework
Documentation
web-token.spomky-labs.com/

Compatibilité en un coup d’œil

JOSE et JWT

Algorithmes JWS
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSA

web-token implémente JWS, JWE, JWK et les jeux de clés JWK sous forme de composants distincts ; web-token/jwt-library les regroupe, et un bundle Symfony les intègre sous forme de services. C’est le choix en PHP lorsqu’il vous faut JWE, la gestion des jeux de clés JWK ou la liste complète des algorithmes : la version 4.2.3 a signé et vérifié avec HS, RS, PS et ES en 256/384/512 ainsi qu’avec EdDSA, y compris PS384/512 et ES512, qui manquent à firebase/php-jwt. Il n’existe pas de classe d’algorithme ES256K ni de classe Ed25519 entièrement spécifiée.

Les algorithmes sont autorisés explicitement : l’AlgorithmManager transmis à JWSVerifier constitue la liste d’autorisation, et un jeton signé avec tout autre algorithme échoue avec The algorithm "RS256" is not supported. none existe en tant que classe d’algorithme, mais n’est accepté que si vous l’ajoutez au gestionnaire. Un jeton HS256 forgé avec le texte PEM de la clé EC a échoué même avec HS256 autorisé, car une JWK EC ne peut pas servir de clé HMAC. ES256 avec une clé P-384 a produit une signature que le propre vérificateur de la bibliothèque a rejetée.

La vérification et le contrôle des claims sont deux étapes distinctes : JWSVerifier::verifyWithKey() a renvoyé true pour un jeton expiré depuis une heure. Exécutez ensuite un ClaimCheckerManager, listez les claims obligatoires (sans cela, exp n’est pas obligatoire) et passez une tolérance de dérive d’horloge à ExpirationTimeChecker, qui exige aussi une horloge PSR-20 depuis la version 4 ; l’exemple montre toute la séquence.

Une signature ES256 est un r‖s brut de 64 octets, et une signature DER fait renvoyer false à verifyWithKey() ; le débogueur JWT montre laquelle des deux un jeton contient. Les clés sont partout des objets JWK ; JWKFactory les construit à partir de PEM, de certificats ou de paramètres de clé, et le générateur de JWKS produit des jeux de clés dans le même format.

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/web-token-jwt-framework/run.sh a exécuté web-token (JWT Framework) 4.2.3 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

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSA

    Signature et vérification réussies avec HS, RS, PS et ES en 256/384/512 ainsi qu’avec EdDSA ; il n’existe pas de classe d’algorithme ES256K ni Ed25519.

    Relevé par la vérification
    fact jws-algorithms = HS256, HS384, HS512, RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512, EdDSA
    evidence jws-algorithms: ES256K: class Jose\Component\Signature\Algorithm\ES256K exists: false
    evidence jws-algorithms: Ed25519: class Jose\Component\Signature\Algorithm\Ed25519 exists: false
  • 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 fait renvoyer false à verifyWithKey().

    Relevé par la vérification
    fact jws-der-signature = rejected
    evidence jws-der-signature: false
  • Liste d’algorithmes autorisés

    required

    L’AlgorithmManager constitue la liste d’autorisation : un jeton RS256 présenté à un gestionnaire qui ne contient que ES256 a échoué avec The algorithm "RS256" is not supported.

    Relevé par la vérification
    fact jwt-algorithm-allowlist = required
    evidence jwt-algorithm-allowlist: RS256 token with an AlgorithmManager holding only ES256: InvalidArgumentException: The algorithm "RS256" is not supported.
  • Jetons non signés (alg none)

    opt-in

    Un jeton non signé échoue avec The algorithm "none" is not supported., sauf si la classe d’algorithme None est ajoutée au gestionnaire.

    Relevé par la vérification
    fact jwt-alg-none = opt-in
    evidence jwt-alg-none: with an ES256 AlgorithmManager: InvalidArgumentException: The algorithm "none" is not supported.; a None algorithm class exists: true
  • Clé publique utilisée comme secret HMAC

    rejected

    Avec ES256 et HS256 tous deux autorisés, un jeton HS256 dont la clé est le texte PEM a échoué face à la JWK EC.

    Relevé par la vérification
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: AlgorithmManager [ES256, HS256] with the EC JWK: false
  • Courbe vérifiée par rapport à alg

    enforced

    La signature ES256 avec une clé P-384 a réussi, mais le vérificateur de la bibliothèque a rejeté le résultat.

    Relevé par la vérification
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: ES256 with a P-384 key, signing: ok; verifying: false
  • Claims validés au décodage

    no

    JWSVerifier::verifyWithKey() a renvoyé true pour un jeton expiré depuis une heure ; les claims sont contrôlés par un ClaimCheckerManager distinct.

    Relevé par la vérification
    fact jwt-claims-validated-on-decode = no
    evidence jwt-claims-validated-on-decode: JWSVerifier on a token expired an hour ago: ok
  • Décalage d’horloge toléré par défaut

    0s

    ExpirationTimeChecker ne tolère aucune dérive d’horloge, sauf si vous lui en indiquez une : un jeton expiré depuis trois secondes a été rejeté.

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

    no

    Sans exp parmi les claims obligatoires, un jeton qui en était dépourvu a été accepté ; l’ajouter à la liste a levé MissingMandatoryClaimException.

    Relevé par la vérification
    fact jwt-exp-required = no
    evidence jwt-exp-required: ExpirationTimeChecker without exp: ok; with exp listed as mandatory: Jose\Component\Checker\MissingMandatoryClaimException: The following claims are mandatory: exp.

Utilisation

Signer en ES256 via une liste d’autorisation AlgorithmManager, vérifier la signature, puis contrôler exp et iss avec des vérificateurs de claims.
<?php
require 'vendor/autoload.php';

use Jose\Component\Checker\{ClaimCheckerManager, ExpirationTimeChecker, IssuerChecker};
use Jose\Component\Core\AlgorithmManager;
use Jose\Component\KeyManagement\JWKFactory;
use Jose\Component\Signature\Algorithm\ES256;
use Jose\Component\Signature\{JWSBuilder, JWSVerifier};
use Jose\Component\Signature\Serializer\CompactSerializer;

$jwk = JWKFactory::createECKey('P-256');
$algorithms = new AlgorithmManager([new ES256()]); // the allow-list
$payload = json_encode(['sub' => 'user-42', 'iss' => 'https://auth.example.com', 'exp' => time() + 600]);
$jws = (new JWSBuilder($algorithms))->create()->withPayload($payload)->addSignature($jwk, ['alg' => 'ES256'])->build();
$token = (new CompactSerializer())->serialize($jws, 0);

// The signature check does not look at claims: run the claim checkers as a second step.
$parsed = (new CompactSerializer())->unserialize($token);
$valid = (new JWSVerifier($algorithms))->verifyWithKey($parsed, $jwk->toPublic(), 0);
$clock = new class implements Psr\Clock\ClockInterface { public function now(): DateTimeImmutable { return new DateTimeImmutable(); } };
$claims = json_decode($parsed->getPayload(), true);
(new ClaimCheckerManager([new ExpirationTimeChecker($clock, 30), new IssuerChecker(['https://auth.example.com'])]))->check($claims, ['exp', 'iss']);
echo 'valid: ', var_export($valid, true), ', 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 web-token/jwt-library:4.2.3
Commande
php usage.php
La sortie doit correspondre à cette expression régulière
valid: true, sub: user-42

Bibliothèques