本文へスキップ
ecdsa.com
PHP

web-token (JWT Framework)

Florent Morselli による JWT Framework(web-token)は、PHP と Symfony 向けの完全な JOSE 実装です。AlgorithmManager が許可リストとして機能し、クレームのチェックは別のチェッカーが行います。RFC 7518 のすべての署名アルゴリズムと EdDSA に対応していますが、ES256K には対応していません。
ライセンス
MIT
確認したバージョン
4.2.3 ·
ソースコード
github.com/web-token/jwt-framework
ドキュメント
web-token.spomky-labs.com/

互換性の概要

JOSE と JWT

JWS アルゴリズム
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSA

web-token は JWS、JWE、JWK、JWK セットを個別のコンポーネントとして実装しています。web-token/jwt-library はそれらをまとめたもので、Symfony バンドルはそれらをサービスとして組み込みます。JWE、JWK セットの管理、またはすべてのアルゴリズムが必要な場合、PHP ではこのライブラリが選択肢になります。4.2.3 では 256/384/512 の HS、RS、PS、ES と EdDSA で署名と検証ができ、firebase/php-jwt にない PS384/512 と ES512 も含まれます。ES256K と、完全指定の Ed25519 のアルゴリズムクラスはありません。

アルゴリズムは明示的に許可します。JWSVerifier に渡す AlgorithmManager が許可リストで、それ以外のアルゴリズムのトークンは The algorithm "RS256" is not supported. で失敗します。none はアルゴリズムクラスとして存在しますが、マネージャーに追加した場合にのみ受け入れられます。EC 鍵の PEM テキストを使って偽造した HS256 トークンは、HS256 を許可していても失敗しました。EC の JWK は HMAC の鍵として使えないためです。P-384 の鍵で ES256 の署名を行うと、ライブラリ自身の検証処理が拒否する署名が生成されました。

署名の検証とクレームのチェックは別々の処理です。JWSVerifier::verifyWithKey() は、1 時間前に期限切れになったトークンに対しても true を返しました。検証の後で ClaimCheckerManager を実行し、必須のクレームを列挙してください(列挙しなければ exp は必須になりません)。また、ExpirationTimeChecker には許容する時刻のずれを渡してください。バージョン 4 以降は PSR-20 のクロックも必要です。サンプルで一連の手順を示しています。

ES256 の署名は 64 バイトの生の r‖s で、DER 署名を渡すと verifyWithKey() は false を返します。トークンがどちらの形式かは JWT デバッガーで確認できます。鍵はすべて JWK オブジェクトとして扱います。JWKFactory は PEM、証明書、鍵パラメーターから JWK を作成し、JWKS ビルダーも同じ形式の鍵セットを生成します。

事実とその根拠となるチェック

以下の内容はドキュメントからの転記ではありません。2026年10月20日に、チェック content/libraries/verify/web-token-jwt-framework/run.sh がコンテナイメージ php:8.5.11-cli-trixie 内で web-token (JWT Framework) 4.2.3 を実行しました。このページの内容はその出力と一致していなければならず、各事実には該当する出力行を示しています。

  • JWS アルゴリズム

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSA

    256/384/512 の HS、RS、PS、ES と EdDSA で署名と検証ができました。ES256K と Ed25519 のアルゴリズムクラスはありません。

    チェックによる記録
    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
  • ES256 署名のエンコーディング

    raw

    ES256 の署名は 64 バイトの生の r‖s です。

    チェックによる記録
    fact jws-ecdsa-encoding = raw
  • JWS 内の DER 署名

    rejected

    DER エンコードされた ES256 署名を渡すと、verifyWithKey() は false を返します。

    チェックによる記録
    fact jws-der-signature = rejected
    evidence jws-der-signature: false
  • アルゴリズムの許可リスト

    required

    AlgorithmManager が許可リストです。ES256 だけを含むマネージャーで RS256 のトークンを検証すると、The algorithm "RS256" is not supported. で失敗しました。

    チェックによる記録
    fact jwt-algorithm-allowlist = required
    evidence jwt-algorithm-allowlist: RS256 token with an AlgorithmManager holding only ES256: InvalidArgumentException: The algorithm "RS256" is not supported.
  • 署名なしトークン(alg none)

    opt-in

    署名のないトークンは、None アルゴリズムクラスをマネージャーに追加しない限り、The algorithm "none" is not supported. で失敗します。

    チェックによる記録
    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
  • HMAC シークレットとして使われる公開鍵

    rejected

    ES256 と HS256 の両方を許可した状態でも、PEM テキストを鍵にした HS256 トークンは EC の JWK に対して失敗しました。

    チェックによる記録
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: AlgorithmManager [ES256, HS256] with the EC JWK: false
  • alg と曲線の照合

    enforced

    P-384 の鍵による ES256 の署名は成功しましたが、ライブラリの検証処理はその結果を拒否しました。

    チェックによる記録
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: ES256 with a P-384 key, signing: ok; verifying: false
  • デコード時のクレーム検証

    no

    JWSVerifier::verifyWithKey() は、1 時間前に期限切れになったトークンに対して true を返しました。クレームは別の ClaimCheckerManager でチェックします。

    チェックによる記録
    fact jwt-claims-validated-on-decode = no
    evidence jwt-claims-validated-on-decode: JWSVerifier on a token expired an hour ago: ok
  • デフォルトのクロックスキュー

    0s

    ExpirationTimeChecker は、指定しない限り時刻のずれを許容しません。3 秒前に期限切れになったトークンは拒否されました。

    チェックによる記録
    fact jwt-clock-skew = 0s
  • 有効期限クレームの要否

    no

    必須クレームに exp を含めないと、これを持たないトークンも通過しました。含めると MissingMandatoryClaimException がスローされました。

    チェックによる記録
    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.

使い方

AlgorithmManager の許可リストを通して ES256 で署名し、署名を検証してから、クレームチェッカーで exp と iss をチェックします。
<?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";

スニペットのテストハーネスは、すべてのサンプルをコンテナイメージ php:8.5.11-cli-trixie 内でそのまま実行し、出力が一致しなければ実行は失敗します。

セットアップ
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
コマンド
php usage.php
一致すべき出力(正規表現)
valid: true, sub: user-42

ライブラリ