Zum Inhalt springen
ecdsa.com
PHP

web-token (JWT Framework)

Das JWT Framework (web-token) von Florent Morselli, die vollständige JOSE-Implementierung für PHP und Symfony: ein AlgorithmManager als Allowlist, separate Claim-Checker, alle Signaturalgorithmen aus RFC 7518 und dazu EdDSA – aber kein ES256K.
Lizenz
MIT
Geprüfte Version
4.2.3 ·
Quellcode
github.com/web-token/jwt-framework
Dokumentation
web-token.spomky-labs.com/

Kompatibilität auf einen Blick

JOSE und JWT

JWS-Algorithmen
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSA

web-token implementiert JWS, JWE, JWK und JWK-Sets als separate Komponenten; web-token/jwt-library bündelt sie, und ein Symfony-Bundle bindet sie als Services ein. Für PHP ist es die richtige Wahl, wenn du JWE, die Verwaltung von JWK-Sets oder die vollständige Liste der Algorithmen brauchst: Version 4.2.3 signierte und prüfte HS, RS, PS und ES bei 256/384/512 sowie EdDSA, einschließlich PS384/512 und ES512, die es in firebase/php-jwt nicht gibt. Eine Algorithmusklasse für ES256K oder für das vollständig spezifizierte Ed25519 gibt es nicht.

Algorithmen werden ausdrücklich zugelassen: Der AlgorithmManager, den du an JWSVerifier übergibst, ist die Allowlist, und ein Token mit einem anderen Algorithmus scheitert mit The algorithm "RS256" is not supported. none existiert als Algorithmusklasse, wird aber nur akzeptiert, wenn du es in den Manager aufnimmst. Ein mit dem PEM-Text des EC-Schlüssels gefälschtes HS256-Token scheiterte selbst bei zugelassenem HS256, weil sich ein EC-JWK nicht als HMAC-Schlüssel verwenden lässt. ES256 mit einem P-384-Schlüssel erzeugte eine Signatur, die der Verifizierer der Bibliothek selbst ablehnte.

Signaturprüfung und Prüfung der Claims sind getrennte Schritte: JWSVerifier::verifyWithKey() gab true für ein Token zurück, das eine Stunde zuvor abgelaufen war. Führe danach einen ClaimCheckerManager aus, liste die Pflicht-Claims auf – sonst ist exp keine Pflicht – und übergib ExpirationTimeChecker eine Toleranz (Drift); seit Version 4 braucht er außerdem eine Uhr nach PSR-20. Das Beispiel zeigt den gesamten Ablauf.

ES256-Signaturen bestehen aus 64 Byte rohem r‖s, und bei einer DER-Signatur gibt verifyWithKey() false zurück; der JWT-Debugger zeigt, welche Form ein Token enthält. Schlüssel sind durchgehend JWK-Objekte; JWKFactory erzeugt sie aus PEM, Zertifikaten oder Schlüsselparametern, und der JWKS-Builder erstellt Schlüsselsätze im selben Format.

Fakten und die Prüfungen dahinter

Nichts im Folgenden ist aus einer Dokumentation abgeschrieben. Das Prüfskript content/libraries/verify/web-token-jwt-framework/run.sh hat web-token (JWT Framework) 4.2.3 am 20. Oktober 2026 im Container-Image php:8.5.11-cli-trixie ausgeführt; die Seite muss mit seiner Ausgabe übereinstimmen, und jeder Fakt führt die zugehörigen Zeilen auf.

  • JWS-Algorithmen

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSA

    Signieren und Prüfen funktionierten mit HS, RS, PS und ES bei 256/384/512 sowie mit EdDSA; Algorithmusklassen für ES256K oder Ed25519 gibt es nicht.

    Von der Prüfung aufgezeichnet
    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
  • Kodierung von ES256-Signaturen

    raw

    ES256-Signaturen bestehen aus 64 Byte rohem r‖s.

    Von der Prüfung aufgezeichnet
    fact jws-ecdsa-encoding = raw
  • DER-Signatur in einer JWS

    rejected

    Bei einer DER-kodierten ES256-Signatur gibt verifyWithKey() false zurück.

    Von der Prüfung aufgezeichnet
    fact jws-der-signature = rejected
    evidence jws-der-signature: false
  • Allowlist der Algorithmen

    required

    Der AlgorithmManager ist die Allowlist: Ein RS256-Token scheiterte an einem Manager, der nur ES256 enthielt, mit The algorithm "RS256" is not supported.

    Von der Prüfung aufgezeichnet
    fact jwt-algorithm-allowlist = required
    evidence jwt-algorithm-allowlist: RS256 token with an AlgorithmManager holding only ES256: InvalidArgumentException: The algorithm "RS256" is not supported.
  • Unsignierte Tokens (alg none)

    opt-in

    Ein unsigniertes Token scheitert mit The algorithm "none" is not supported., sofern die Algorithmusklasse None nicht in den Manager aufgenommen wird.

    Von der Prüfung aufgezeichnet
    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
  • Öffentlicher Schlüssel als HMAC-Secret

    rejected

    Mit ES256 und HS256 als zulässigen Algorithmen scheiterte ein HS256-Token mit dem PEM-Text als Schlüssel am EC-JWK.

    Von der Prüfung aufgezeichnet
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: AlgorithmManager [ES256, HS256] with the EC JWK: false
  • Abgleich der Kurve mit alg

    enforced

    Das Signieren mit ES256 und einem P-384-Schlüssel gelang, doch der Verifizierer der Bibliothek lehnte das Ergebnis ab.

    Von der Prüfung aufgezeichnet
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: ES256 with a P-384 key, signing: ok; verifying: false
  • Validierung der Claims beim Dekodieren

    no

    JWSVerifier::verifyWithKey() gab true für ein Token zurück, das eine Stunde zuvor abgelaufen war; die Claims prüft ein separater ClaimCheckerManager.

    Von der Prüfung aufgezeichnet
    fact jwt-claims-validated-on-decode = no
    evidence jwt-claims-validated-on-decode: JWSVerifier on a token expired an hour ago: ok
  • Standardtoleranz für Uhrabweichungen

    0s

    ExpirationTimeChecker lässt keine Toleranz (Drift) zu, sofern ihm keine übergeben wird: Ein Token, das drei Sekunden zuvor abgelaufen war, wurde abgelehnt.

    Von der Prüfung aufgezeichnet
    fact jwt-clock-skew = 0s
  • Ablauf-Claim erforderlich

    no

    Ohne exp unter den Pflicht-Claims bestand ein Token ohne diesen Claim die Prüfung; stand der Claim in der Liste, wurde MissingMandatoryClaimException ausgelöst.

    Von der Prüfung aufgezeichnet
    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.

Verwendung

Signiere mit ES256 über einen AlgorithmManager als Allowlist, prüfe die Signatur und prüfe danach exp und iss mit Claim-Checkern.
<?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";

Die Snippet-Testumgebung führt jedes Beispiel unverändert im Container-Image php:8.5.11-cli-trixie aus, und der Lauf schlägt fehl, wenn die Ausgabe nicht übereinstimmt.

Einrichtung
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
Befehl
php usage.php
Ausgabe muss passen (regulärer Ausdruck)
valid: true, sub: user-42

Bibliotheken