Zum Inhalt springen
ecdsa.com
PHP

lcobucci/jwt

lcobucci/jwt von Luís Cobucci, die JWT-Bibliothek hinter Laravel Passport und vielen PHP-Frameworks: Jede Prüfung ist ein expliziter Constraint, assert() lässt sich ohne einen Constraint nicht ausführen, und Schlüssellängen werden durchgesetzt – aber RSA-PSS fehlt.
Lizenz
BSD-3-Clause
Geprüfte Version
5.6.1 ·
Quellcode
github.com/lcobucci/jwt
Dokumentation
lcobucci-jwt.readthedocs.io/en/stable/

Kompatibilität auf einen Blick

JOSE und JWT

JWS-Algorithmen
HS256HS384HS512RS256RS384RS512ES256ES384ES512EdDSA

lcobucci/jwt trennt Parsen, Signieren und Validierung: Eine Configuration enthält den Signierer und die Schlüssel, parser()->parse() liest ein Token, ohne irgendetwas zu prüfen, und validator()->assert() wendet die Constraints an, die du übergibst. Ein Aufruf von assert() ohne Constraints wirft NoConstraintsGiven – so schreibst du kaum versehentlich eine Validierung, die nichts prüft.

SignedWith legt Signierer und Schlüssel fest: Ein RS256-Token und ein mit dem PEM-Text des EC-Schlüssels gefälschtes HS256-Token scheiterten beide mit Token signer mismatch. Unsignierte Tokens lassen sich nicht einmal parsen (The JWT string is missing the Signature part). Schlüssellängen werden schon beim Signieren durchgesetzt – ES256 lehnte einen P-384-Schlüssel ab (The length of the provided key is different than 256 bits), HS256 einen 40-Bit-Schlüssel –, sodass fehlerhafte Tokens gar nicht erst entstehen.

Auch die Zeitprüfungen sind Constraints und laufen daher nur, wenn sie aufgeführt sind: Ein Token, das eine Stunde zuvor abgelaufen war, bestand SignedWith allein. LooseValidAt prüft exp, nbf und iat, sofern vorhanden, und akzeptierte ein Token ohne exp; StrictValidAt verlangt alle drei – ein Token ohne iat scheiterte mit "Issued At" claim missing, eines mit iat und exp, aber ohne nbf mit "Not Before" claim missing. Beide nehmen ein optionales Toleranzintervall (Leeway) entgegen, standardmäßig null. Das Beispiel setzt alle drei Claims und prüft Signatur, Aussteller und strikte Gültigkeit in einem Aufruf.

Version 5.6.1 unterstützt HS, RS und ES bei 256/384/512 sowie EdDSA; es gibt keine Signierer für PS* und kein ES256K, sodass Tokens von Ausstellern, die RSA-PSS verwenden, web-token brauchen. ES256-Signaturen bestehen aus 64 Byte rohem r‖s, und eine DER-Signatur scheitert mit Token signature mismatch; der JWT-Debugger zeigt, welche Kodierung ein Token enthält.

Fakten und die Prüfungen dahinter

Nichts im Folgenden ist aus einer Dokumentation abgeschrieben. Das Prüfskript content/libraries/verify/lcobucci-jwt/run.sh hat lcobucci/jwt 5.6.1 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

    HS256HS384HS512RS256RS384RS512ES256ES384ES512EdDSA

    Signieren und Prüfen funktionierten mit HS, RS und ES bei 256/384/512 sowie mit EdDSA; Signierer-Klassen für RSA-PSS oder ES256K gibt es nicht.

    Von der Prüfung aufgezeichnet
    fact jws-algorithms = HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384, ES512, EdDSA
    evidence jws-algorithms: PS256 signer class Lcobucci\JWT\Signer\Rsa\Pss\Sha256 exists: false
    evidence jws-algorithms: ES256K signer class Lcobucci\JWT\Signer\Ecdsa\Sha256K 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

    Eine DER-kodierte ES256-Signatur scheitert mit Token signature mismatch.

    Von der Prüfung aufgezeichnet
    fact jws-der-signature = rejected
    evidence jws-der-signature: Lcobucci\JWT\Validation\RequiredConstraintsViolated: The token violates some mandatory constraints, details: - Token signature mismatch
  • Allowlist der Algorithmen

    required

    SignedWith legt den Signierer fest: Ein RS256-Token scheiterte an einem ES256-Signierer mit Token signer mismatch, und assert() ohne Constraints wirft NoConstraintsGiven.

    Von der Prüfung aufgezeichnet
    fact jwt-algorithm-allowlist = required
    evidence jwt-algorithm-allowlist: RS256 token with SignedWith(ES256 signer, EC key): Lcobucci\JWT\Validation\RequiredConstraintsViolated: The token violates some mandatory constraints, details: - Token signer mismatch
    evidence jwt-algorithm-allowlist: assert() with no constraints: Lcobucci\JWT\Validation\NoConstraintsGiven: No constraint given.
  • Unsignierte Tokens (alg none)

    rejected

    Ein unsigniertes Token lässt sich nicht parsen: The JWT string is missing the Signature part.

    Von der Prüfung aufgezeichnet
    fact jwt-alg-none = rejected
    evidence jwt-alg-none: parsing an unsigned token: Lcobucci\JWT\Token\InvalidTokenStructure: The JWT string is missing the Signature part
  • Öffentlicher Schlüssel als HMAC-Secret

    rejected

    Ein HS256-Token mit dem PEM-Text als Schlüssel bestand SignedWith für den ES256-Signierer nicht und scheiterte mit Token signer mismatch.

    Von der Prüfung aufgezeichnet
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: SignedWith(ES256 signer, EC key): Lcobucci\JWT\Validation\RequiredConstraintsViolated: The token violates some mandatory constraints, details: - Token signer mismatch
  • Abgleich der Kurve mit alg

    enforced

    Der ES256-Signierer lehnte einen P-384-Schlüssel ab: The length of the provided key is different than 256 bits, 384 bits provided.

    Von der Prüfung aufgezeichnet
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: ES256 signer with a P-384 key: Lcobucci\JWT\Signer\InvalidKeyProvided: The length of the provided key is different than 256 bits, 384 bits provided
  • Validierung der Claims beim Dekodieren

    no

    Parsen und SignedWith prüfen keine Zeitangaben: Ein Token, das eine Stunde zuvor abgelaufen war, kam durch.

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

    0s

    LooseValidAt und StrictValidAt lassen keine Toleranz (Leeway) zu, sofern ihnen kein Intervall übergeben wird.

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

    no

    LooseValidAt akzeptierte ein Token ohne exp; StrictValidAt verlangt iat, nbf und exp.

    Von der Prüfung aufgezeichnet
    fact jwt-exp-required = no
    evidence jwt-exp-required: LooseValidAt without exp: ok; StrictValidAt: Lcobucci\JWT\Validation\RequiredConstraintsViolated: The token violates some mandatory constraints, details: - "Issued At" claim missing
    evidence jwt-exp-required: StrictValidAt with iat and exp but no nbf: Lcobucci\JWT\Validation\RequiredConstraintsViolated: The token violates some mandatory constraints, details: - "Not Before" claim missing
  • Kurzes HMAC-Secret

    rejected

    HS256 lehnte einen 5-Byte-Schlüssel ab: Key provided is shorter than 256 bits, only 40 bits provided.

    Von der Prüfung aufgezeichnet
    fact jwt-hmac-short-secret = rejected
    evidence jwt-hmac-short-secret: 5-byte HS256 key: Lcobucci\JWT\Signer\InvalidKeyProvided: Key provided is shorter than 256 bits, only 40 bits provided

Verwendung

Stelle ein ES256-Token aus und prüfe Signatur, Aussteller und strikte Gültigkeit mit 30 Sekunden Toleranz.
<?php
require 'vendor/autoload.php';

use Lcobucci\Clock\SystemClock;
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer\Ecdsa\Sha256;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Validation\Constraint\{IssuedBy, SignedWith, StrictValidAt};

$key = openssl_pkey_new(['private_key_type' => OPENSSL_KEYTYPE_EC, 'curve_name' => 'prime256v1']);
openssl_pkey_export($key, $privatePem);
$private = InMemory::plainText($privatePem);
$public = InMemory::plainText(openssl_pkey_get_details($key)['key']);

$config = Configuration::forAsymmetricSigner(new Sha256(), $private, $public);
$now = new DateTimeImmutable();
$token = $config->builder()->issuedBy('https://auth.example.com')->relatedTo('user-42')
    ->issuedAt($now)->canOnlyBeUsedAfter($now)->expiresAt($now->modify('+10 minutes'))
    ->getToken($config->signer(), $config->signingKey());

// Every check is an explicit constraint; StrictValidAt requires iat, nbf and exp.
$parsed = $config->parser()->parse($token->toString());
$config->validator()->assert($parsed,
    new SignedWith($config->signer(), $config->verificationKey()),
    new IssuedBy('https://auth.example.com'),
    new StrictValidAt(SystemClock::fromUTC(), new DateInterval('PT30S')));
echo 'sub: ', $parsed->claims()->get('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 lcobucci/jwt:5.6.1 lcobucci/clock:3.6.0
Befehl
php usage.php
Ausgabe muss passen (regulärer Ausdruck)
sub: user-42

Bibliotheken