JWT - JSON Web Tokens
Xmf\Jwt 名前空間はXOOPSモジュール用のJSON Web Token (JWT) サポートを提供します。JWTはセキュアでステートレスな認証を可能にし、特にAJAXリクエスト保護に有用です。
JSON Web Tokenとは?
Section titled “JSON Web Tokenとは?”JSON Web Tokenは、クレーム (データ) のセットを公開する標準的な方法で、クレームが改ざんされていないことを暗号検証する機能を持つテキスト文字列です。詳細な仕様については、以下を参照:
- 署名済み: トークンは暗号的に署名されており、改ざんを検出
- 自己完結: すべての必要な情報がトークン内に含まれている
- ステートレス: サーバー側のセッション保存が不要
- 有効期限設定可: トークンは有効期限を含めることが可能
注: JWTは署名されていますが、暗号化されていません。データはBase64エンコードされており、可視です。機密データを隠すのではなく、整合性検証用にJWTを使用してください。
XOOPS でなぜ JWT を使用するのか?
Section titled “XOOPS でなぜ JWT を使用するのか?”AJAX トークンの問題
Section titled “AJAX トークンの問題”XOOPSフォームはCSRF保護用にnonceトークンを使用します。しかし、nonceはAJAXではうまく機能しません。理由は:
- 単一使用: Nonceは通常1回の提出でのみ有効
- 非同期の問題: 複数のAJAXリクエストは順序が乱れる可能性
- 更新の複雑さ: トークンを非同期に更新する信頼できる方法がない
- コンテキストバインディング: 標準トークンは、どのスクリプトがそれを発行したかを検証しない
JWT の利点
Section titled “JWT の利点”JWTはこれらの問題を以下によって解決:
- 有効期限 (
expクレーム) を含めることで時間制限された有効性 - カスタムクレームをサポートして、トークンを特定のスクリプトにバインド
- 有効期間内の複数リクエストを有効化
- トークン発行元の暗号検証を提供
JsonWebToken
Section titled “JsonWebToken”Xmf\Jwt\JsonWebToken クラスはトークンの作成とデコードを処理します。
use Xmf\Jwt\JsonWebToken;use Xmf\Jwt\KeyFactory;
// キーを作成$key = KeyFactory::build('my_application_key');
// JsonWebTokenインスタンスを作成$jwt = new JsonWebToken($key, 'HS256');
// トークンを作成$payload = ['user_id' => 123, 'aud' => 'myaction'];$token = $jwt->create($payload, 300); // 300秒で期限切れ
// トークンをデコードおよび検証$assertClaims = ['aud' => 'myaction'];$decoded = $jwt->decode($tokenString, $assertClaims);new JsonWebToken($key, $algorithm)
新しいJWTハンドラーを作成。
$key:Xmf\Key\KeyAbstractオブジェクト$algorithm: 署名アルゴリズム (デフォルト: ‘HS256’)
create($payload, $expirationOffset)
署名済みトークン文字列を作成。
$payload: クレームの配列$expirationOffset: 有効期限までの秒数 (オプション)
decode($jwtString, $assertClaims)
トークンをデコードおよび検証。
$jwtString: デコードするトークン$assertClaims: 検証するクレーム (なしの場合は空配列)- 返す: stdClassペイロードまたは無効の場合はfalse
setAlgorithm($algorithm)
署名/検証アルゴリズムを変更。
TokenFactory
Section titled “TokenFactory”Xmf\Jwt\TokenFactory はトークン作成の便利な方法を提供します。
use Xmf\Jwt\TokenFactory;
// 自動キー処理でトークンを作成$claims = [ 'aud' => 'myaction.php', 'user_id' => $userId, 'item_id' => $itemId];
$token = TokenFactory::build('my_key', $claims, 120);// トークンは120秒で期限切れTokenFactory::build($key, $payload, $expirationOffset)
$key: キー名文字列またはKeyAbstractオブジェクト$payload: クレームの配列$expirationOffset: 秒単位の有効期限
失敗時の例外: DomainException, InvalidArgumentException, UnexpectedValueException
TokenReader
Section titled “TokenReader”Xmf\Jwt\TokenReader クラスは、様々なソースからのトークン読み込みを簡素化します。
use Xmf\Jwt\TokenReader;
$assertClaims = ['aud' => 'myaction.php'];
// 文字列から$payload = TokenReader::fromString('my_key', $tokenString, $assertClaims);
// クッキーから$payload = TokenReader::fromCookie('my_key', 'token_cookie', $assertClaims);
// リクエストパラメータから$payload = TokenReader::fromRequest('my_key', 'token', $assertClaims);
// Authorizationヘッダーから (Bearerトークン)$payload = TokenReader::fromHeader('my_key', $assertClaims);すべてのメソッドはペイロードを stdClass として、または無効の場合は false として返します。
KeyFactory
Section titled “KeyFactory”Xmf\Jwt\KeyFactory は暗号キーを作成および管理します。
use Xmf\Jwt\KeyFactory;
// キーを構築 (存在しない場合は作成)$key = KeyFactory::build('my_application_key');
// カスタムストレージで$storage = new \Xmf\Key\FileStorage('/custom/path');$key = KeyFactory::build('my_key', $storage);キーは永続的に保存されます。デフォルトストレージはファイルシステムを使用します。
AJAX保護の例
Section titled “AJAX保護の例”JWT保護されたAJAXを示す完全な例です。
ページスクリプト (トークンを生成)
Section titled “ページスクリプト (トークンを生成)”<?phpuse Xmf\Jwt\TokenFactory;use Xmf\Jwt\TokenReader;use Xmf\Module\Helper;use Xmf\Request;
require_once dirname(dirname(__DIR__)) . '/mainfile.php';
// 含めて検証するクレーム$assertClaims = ['aud' => basename(__FILE__)];
// これはAJAXリクエストか?$isAjax = (0 === strcasecmp(Request::getHeader('X-Requested-With', ''), 'XMLHttpRequest'));
if ($isAjax) { // AJAXリクエストを処理 $GLOBALS['xoopsLogger']->activated = false;
// Authorizationヘッダーからトークンを検証 $token = TokenReader::fromHeader('ajax_key', $assertClaims);
if (false === $token) { http_response_code(401); echo json_encode(['error' => 'Not authorized']); exit; }
// トークンは有効 - リクエストを処理 $action = Request::getCmd('action', ''); $itemId = isset($token->item_id) ? $token->item_id : 0;
// AJAXロジックをここに $response = ['success' => true, 'item_id' => $itemId];
http_response_code(200); header('Content-Type: application/json'); echo json_encode($response); exit;}
// 通常のページリクエスト - トークンを生成してページを表示require_once XOOPS_ROOT_PATH . '/header.php';
$helper = Helper::getHelper(basename(__DIR__));
// クレーム付きのトークンを作成$claims = array_merge($assertClaims, [ 'item_id' => 42, 'user_id' => $GLOBALS['xoopsUser']->getVar('uid')]);
// トークンは2分間有効$token = TokenFactory::build('ajax_key', $claims, 120);
// AJAX呼び出し用のJavaScript$script = <<<JS<script>function performAction(action) { $.ajax({ url: window.location.href, method: 'POST', data: { action: action }, dataType: 'json', beforeSend: function(xhr) { xhr.setRequestHeader('Authorization', 'Bearer {$token}'); }, success: function(data) { if (data.success) { console.log('Action completed:', data); // UIを更新 } }, error: function(xhr, status, error) { if (xhr.status === 401) { alert('Session expired. Please refresh the page.'); } else { alert('An error occurred: ' + error); } } });}</script>JS;
echo $script;echo '<button onclick="performAction(\'save\')">Save Item</button>';echo '<button onclick="performAction(\'delete\')">Delete Item</button>';
require_once XOOPS_ROOT_PATH . '/footer.php';ベストプラクティス
Section titled “ベストプラクティス”トークン有効期限
Section titled “トークン有効期限”使用例に基づいて適切な有効期限を設定:
// 機密操作用の短期有効 (2分)$token = TokenFactory::build('key', $claims, 120);
// 一般的なページインタラクション用のより長い期間 (30分)$token = TokenFactory::build('key', $claims, 1800);クレーム検証
Section titled “クレーム検証”aud (audience) クレームを常に検証して、トークンが意図されたスクリプトで使用されることを保証:
// 作成時$claims = ['aud' => 'process_order.php', 'order_id' => 123];
// 検証時$assertClaims = ['aud' => 'process_order.php'];$token = TokenReader::fromHeader('key', $assertClaims);異なる目的には説明的なキー名を使用:
// 異なる機能用に別のキーを使用$orderToken = TokenFactory::build('order_processing', $orderClaims, 300);$commentToken = TokenFactory::build('comment_system', $commentClaims, 600);エラーハンドリング
Section titled “エラーハンドリング”use Xmf\Jwt\TokenFactory;use Xmf\Jwt\TokenReader;
try { $token = TokenFactory::build('my_key', $claims, 300);} catch (\DomainException $e) { // 無効なアルゴリズム error_log('JWT Error: ' . $e->getMessage());} catch (\InvalidArgumentException $e) { // 無効な引数 error_log('JWT Error: ' . $e->getMessage());} catch (\UnexpectedValueException $e) { // 予期しない値 error_log('JWT Error: ' . $e->getMessage());}
// トークン読み込みは失敗時にfalseを返す (例外ではない)$payload = TokenReader::fromHeader('my_key', $assertClaims);if ($payload === false) { // トークン無効、期限切れ、または改ざん}トークン転送方法
Section titled “トークン転送方法”Authorizationヘッダー (推奨)
Section titled “Authorizationヘッダー (推奨)”xhr.setRequestHeader('Authorization', 'Bearer ' + token);$payload = TokenReader::fromHeader('key', $assertClaims);// トークン付きクッキーを設定setcookie('api_token', $token, time() + 300, '/', '', true, true);
// クッキーから読み込み$payload = TokenReader::fromCookie('key', 'api_token', $assertClaims);リクエストパラメータ
Section titled “リクエストパラメータ”$.ajax({ url: 'handler.php', data: { token: token, action: 'save' }});$payload = TokenReader::fromRequest('key', 'token', $assertClaims);セキュリティの考慮
Section titled “セキュリティの考慮”- HTTPSを使用: トークン傍受を防ぐために常にHTTPSを使用
- 短い有効期限: 実用的な最短の有効期限を使用
- 特定のクレーム: トークンを特定のコンテキストにバインドするクレームを含める
- サーバー側検証: 常にサーバー側でトークンを検証
- 機密データは保存しない: トークンは読み込み可能 (暗号化されていない) ことを覚えておく
APIリファレンス
Section titled “APIリファレンス”Xmf\Jwt\JsonWebToken
Section titled “Xmf\Jwt\JsonWebToken”| メソッド | 説明 |
|---|---|
__construct($key, $algorithm) | JWTハンドラーを作成 |
setAlgorithm($algorithm) | 署名アルゴリズムを設定 |
create($payload, $expiration) | 署名済みトークンを作成 |
decode($token, $assertClaims) | トークンをデコードおよび検証 |
Xmf\Jwt\TokenFactory
Section titled “Xmf\Jwt\TokenFactory”| メソッド | 説明 |
|---|---|
build($key, $payload, $expiration) | トークン文字列を作成 |
Xmf\Jwt\TokenReader
Section titled “Xmf\Jwt\TokenReader”| メソッド | 説明 |
|---|---|
fromString($key, $token, $claims) | 文字列からデコード |
fromCookie($key, $name, $claims) | クッキーからデコード |
fromRequest($key, $name, $claims) | リクエストからデコード |
fromHeader($key, $claims, $header) | ヘッダーからデコード |
Xmf\Jwt\KeyFactory
Section titled “Xmf\Jwt\KeyFactory”| メソッド | 説明 |
|---|---|
build($name, $storage) | キーを取得または作成 |
- ../Basics/XMF-Request - リクエスト処理
- ../XMF-Framework - フレームワーク概要
- Database - データベースユーティリティ
#xmf #jwt #security #ajax #authentication #tokens