PHP

Laravel5 PHP-JWT Firebase製のJWTライブラリで認証

Laravel

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>
  1. ヘッダー(header)
  2. 属性情報(claim)
  3. 署名(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の中身を見たい場合(デバッガ)

下記のサービスで簡単に確認ができます。

jwt.io

 

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

 

 

Amazonおすすめ

iPad 9世代 2021年最新作

iPad 9世代出たから買い替え。安いぞ!🐱 初めてならiPad。Kindleを外で見るならiPad mini。ほとんどの人には通常のiPadをおすすめします><

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)