
Firebase謹製のPHP-JWTライブラリを利用した案件が多いからまとめる。
JWTライブラリにも色々種類があるのだ。
もくじ
payload
@see Wikipedia
| コード | 名称 | 説明 | 
| iss | issuer | トークンの発行者 | 
| sub | Subject | トークンの主題 | 
| aud | Audience | トークンが意図している受信者の識別子 | 
| exp | Expiration Time | 有効期限。JWTが失効する日時 | 
| nbf | Not Before | トークンが有効になる日時 | 
| iat | issued at | トークンの発行日時 | 
| jti | JWT ID | 発行者ごとトークンごとに一意な識別子 | 
インストール
$ composer require firebase/php-jwt
// docker-composeの場合はこっち
$ docker-compose exec php-fpm composer require firebase/php-jwt
# mkdir jwt_keys # cd jwt_keys
秘密鍵作成
# openssl genrsa -out private.pem 2048
公開鍵作成
# openssl rsa -in private.pem -outform PEM -pubout -out public.pem writing RSA key
# chmod 600 private.pem public.pem
Http/Kernel.php
    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
+       'auth.api' => \App\Http\Middleware\ApiTokenChecker::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    ];
アクセストークンの設計
設計例
● Header
{
  "typ": "JWT",  // 固定値
  "alg": "HS256" // 署名アルゴリズム HS256で良い
  "kid": "hohhofdohofeh" // key ID
}
● Payload
{
  "sub" : “12", // ユーザid
  "iat": 1356999524,
  "exp": 1360819380
}
● 有効期限
 ・AccessToken = n分後で有効期限
 ・RefreshToken = m週間で有効期限
JWTはピリオド2つで繋がったデータ構造になっています。
<Base64エンコードしたHeader>.<Base64エンコードしたClaims>.<Base64エンコードしたSignature>
- ヘッダー(header)
- 属性情報(claim)
- 署名(alg)
備考
- JWTは暗号化されているわけではない。
- JWTの文字列にURLに利用できない文字列を利用してはいけない
Header
{
  "alg": "HS256",
  "typ": "JWT",
  "kid": "kjfjs0543939acf73be64604d49a097189a"
}
kidを含めるとメンテナンス性が向上する。
kidがないと秘密鍵の交換の際に全員がログアウトすることになる。
Payload(ユーザ情報など)
{
  “user_id”: “12”, // ユーザid
  "iat": 1356999524, // 発行日時
  "exp": 1360819380 // 有効期限
  "kid": "fakjo09jlksfjkslna.nb" // kid(Optional)
}
絶対にペイロードにパスワード等の機密情報を含めてはいけない
- idは含める
- kidは秘密鍵が漏洩した際に差し変える為に必要
Signature
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
data = base64urlEncode( header ) + ‘.’ + base64urlEncode( payload )
signature = Hash( data, secret )
data + signature
JWTの中身を見たい場合(デバッガ)
下記のサービスで簡単に確認ができます。
Sample
$user_id = $_GET['user_id'];
$password = $_GET['password'];
// 認証
if(!is_invalid($user_id, $password)) {
    return json_encode(array(
      'message' => 'Invalid User.'
    ));
}
function createAccessToken(int $minutes = 15, string $user_id): array
{
    $current_time = time();
    $expire = $current_time + ($minutes * 60); // 15分
    $claims = array (
      'user_id' => $user_id,
      'iat' => $current_time,
      'exp' => $expire,
    );
    /* 秘密鍵の取得 */
    $private_key = file_get_contents(__DIR__ . '/keys/jwt.key');
    /* エンコード */
    $jwt = JWT::encode($claims, $private_key, 'HS256');
    return json_encode(array(
      'message' => 'Success.',
      'jwt' => $jwt
    ));
}
function createRefreshToken(int $minutes = 40320, string $user_id): array
{
    $current_time = time();
    $expire = $current_time + ($minutes * 60); // デフォルト4週間
    $claims = array (
      'user_id' => $user_id,
      'iat' => $current_time,
      'exp' => $expire,
    );
    /* 秘密鍵の取得 */
    $private_key = file_get_contents(__DIR__ . '/keys/jwt.key');
    /* エンコード */
    $jwt = JWT::encode($claims, $private_key, 'HS256');
    return json_encode(array(
      'message' => 'Success.',
      'jwt' => $jwt
    ));
}
セキュリティでの注意
- alg = noneにして検証を回避できる脆弱性が存在する
 1. alg = none
 2. ペイロードを改ざん
 3. 署名を削除
対策
- algをホワイトリスト形式で制限する
- RS256(公開鍵認証方式)のみ扱うようにする
@see
- laravel firebase/php-jwt token验证
 // firebase/php-jwtでの実装が見れる
- JWT 入門
- JSON Web Token(JWT)のClaimについて
 // Payload設計を見た。
- JSON Web Token (JWT) とは何?
- JWT(JSON Web Token)の「仕組み」と「注意点」
 // セキュリティでの注意点の参考
- 🌟WebCrypto APIでJSON Web Tokenの検証を試してみる
- https://jwt.io/introduction/

