12
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【JWT】JWTとは?〜PHPライブラリでJWTを使ってみる

Last updated at Posted at 2022-06-01

JWTとは

  • Json Web Tokenの略称、ジョットと読む
  • JSON形式のデータをURLセーフな方法で送信するための技術
  • 電子署名によりデータの改ざんを検知でき、ユーザー認証などで利用されている
  • 仕様はRFC7519に定められている

auth0のJWT特設サイトにサンプルとデバッガが用意されています。

JWTの構成

下記の要素をピリオドで連結した文字列です。

  • Base64URLエンコードされたヘッダー
  • Base64URLエンコードされたペイロード
  • 署名

auth0のJWT特設サイトより

# Base64URLエンコードされたペイロード.
# Base64URLエンコードされたペイロード.
# 署名
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.
tRIxJAB-GnSnpLRyw5YMZcXAdFG2d4pbc0pkzD-FqW6SufaVb6l7D7Fh-HIqzNpBiiIZM828uB6fKqY_SR1Jx8akV9RvN53kirNRtABhPNfaVehV3CMqU9Nn2jHU1x8WLmiEeF8f97CzTZ7WLgfIx1b44mzQkVzxcTBdDhiOkgHJO-HpOY1javKV0JgajEWFY_wq80nnHDlk_HdB8SLN3AhPvwsMPGMeIW0ICQoekNxqM_4HX_QKPf9Url2EY9V0TenrfL3W5arkBbgTk-GEUchLNrLa3LhxITY1TrLVkPv17MLbYcp3nip2p-_7Mz5hjY94rLkltqBfxv6nKyWN1g

ヘッダー(header)

署名の生成に利用したアルゴリズムの情報です。
通常はalgtypの2つで構成されます。

alg(algolosm)

  • 署名アルゴリズム
  • HS256、RS256、ES256など

typ(type)

  • トークンのタイプ
  • JWT一択

ペイロード(payload)

データ本体です。
クレーム(Claim)と呼ばれるフィールドと値が対でセットされています。クレームは下記3種類に分けられます。

登録済みクレーム(Registered Claim)

  • 標準のクレーム
  • 利用が推奨されているが、必須ではない
  • 詳細はこちら

パブリッククレーム(Public Claim)

プライベートクレーム(Private Claim)

  • 登録済みクレーム、パブリッククレーム以外のクレーム
  • クレームの名前は使用者が自由に設定できる

署名(signature)

下記の要素を秘密鍵を使用し指定したアルゴリズム(alg)で暗号化した文字列です。

  • Base64URLエンコードされたヘッダー
  • Base64URLでエンコードされたペイロード

サンプルを読んでみよう

auth0のJWT特設サイトのデバッガでRS256を指定した時に表示されるサンプルです。
上記の説明を踏まえるとトークンの構成がよく分かります。

# ヘッダー
{
  "alg": "RS256",      # 署名アルゴリズム
  "typ": "JWT"         # トークンのタイプ
}

# ペイロード
{
  "sub": "1234567890", # 登録済みクレーム
  "name": "John Doe",  # パブリッククレーム
  "admin": true,       # プライベートクレーム
  "iat": 1516239022    # 登録済みクレーム
}

PHPライブラリでJWTを使ってみよう

PHPでJWTを使ってみます。
今回はfirebase/php-jwtlcobucci/jwtの2つのライブラリでトークンの生成・検証・データ取得処理を実装しました。

使用感も書きましたので実装の参考にしてください。

firebase/php-jwt

  • 「php jwt」のGoogle検索でトップ、情報量が多い
  • シンプルな構成でキャッチアップが楽
  • シンプルな反面、細かな検証ルールを追加すると実装方法が人それぞれになりやすい
require_once '../vendor/autoload.php';

use Firebase\JWT\JWT;
use Firebase\JWT\Key;

class FirebaseJWT
{
    private string $signer;
    private string $private_key;
    private string $public_key;

    public function __construct()
    {
        $this->signer = 'RS256';
        $this->private_key = file_get_contents('private.pem');
        $this->public_key = file_get_contents('public.pem');
    }

    // tokenの生成
    public function createToken(): string
    {
        $payload = [
            'iss' => 'hogehoge',
            'sub' => 'Hello!',
            'name' => 'fugafuga',
            'admin' => true,
            'iat' => time()
        ];

        return JWT::encode($payload, $this->private_key, $this->signer);
    }

    // tokenの検証
    public function checkToken(string $jwt): bool
    {
        try {
            // 署名検証
            $decoded = JWT::decode($jwt, new Key($this->public_key, $this->signer));
            // iss検証
            if ($decoded->iss !== 'hogehoge') {
                throw new Exception('iss error');
            }
            // sub検証
            if ($decoded->sub !== 'Hello!') {
                throw new Exception('sub error');
            }
        } catch (Exception $e) {
            return false;
        }

        return true;
    }

    // tokenからデータを取得
    public function getData(string $jwt): array
    {
        return (array)JWT::decode($jwt, new Key($this->public_key, $this->signer));
    }
}

$firebase_jwt = new FirebaseJWT();
$jwt = $firebase_jwt->createToken();
$firebase_jwt->checkToken($jwt);
$firebase_jwt->getData($jwt);

lcobucci/jwt

  • 鍵の参照ロジック、署名やパブリッククレームの検証ロジックがクラス化されている
  • 検証ロジックの集合をバリデーションルールとして一括で扱うことができる
  • オブジェクト指向が好きな人向き
  • 実装方法が固定されやすい
  • キャッチアップにやや時間がかかる
require_once '../vendor/autoload.php';

use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Signer\RSA\Sha256;
use Lcobucci\JWT\Validation\Constraint\SignedWith;
use Lcobucci\JWT\Validation\Constraint\IssuedBy;
use Lcobucci\JWT\Validation\Constraint\RelatedTo;

class LcobucciJWT
{
    private Configuration $config;

    public function __construct()
    {
        $this->config = Configuration::forAsymmetricSigner(
            new Sha256(),
            InMemory::file('private.pem'),
            InMemory::file('public.pem')
        );
    }

    // tokenの生成
    public function createToken(): string
    {
        $token = $this->config->builder()
            ->issuedBy('hogehoge')              // iss
            ->relatedTo('Hello!')               // sub
            ->withClaim('name', 'fugafuga')     // name(パブリッククレーム)
            ->withClaim('admin', true)          // admin(プライベートクレーム)
            ->issuedAt(new DateTimeImmutable()) // iat
            ->getToken($this->config->signer(), $this->config->signingKey());

        return $token->toString();
    }

    // tokenの検証
    public function checkToken(string $jwt): bool
    {
        $token = $this->config->parser()->parse($jwt);
        $this->config->setValidationConstraints(...[
            // 署名検証
            new SignedWith($this->config->signer(), $this->config->verificationKey()),
            // iss検証
            new IssuedBy('hogehoge'),                                       
            // sub検証
            new RelatedTo('Hello!')                                             
        ]);

        // バリデーションエラーを例外で返したい場合はassert()を使用する
        return $this->config->validator()->validate($token, ...$this->config->validationConstraints());
    }

    // tokenからデータを取得
    public function getData(string $jwt): array
    {
        $token = $this->config->parser()->parse($jwt);

        return $token->claims()->all();
    }
}

$lcobucci_jwt = new LcobucciJWT();
$jwt = $lcobucci_jwt->createToken();
$lcobucci_jwt->checkToken($jwt);
$lcobucci_jwt->getData($jwt);

感想

JWTはauth0の特設サイトやライブラリが充実しているため、手軽に検証ができます。
ライブラリはドキュメントの他にテストコードも実装の参考になりました!

採用PR

弊社で一緒に働く仲間を募集しています。
全てのオタクを幸せにしたい方、是非ご覧ください!

12
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?