はじめに
認証について調べていると、JWTは避けては通れません。
しかし、わかりにくいところでもあります。
JWTの利用方法について、実装しながら勉強しました。
JWTについて
ここでは簡単にまとめます。詳しくはほかの方の記事をご覧ください。
※飛ばしたい人はこちら→ 実装
JWTとは
JWTは「JSON Web Token」の略称で、読み方は「ジョット(jot)」1です。
その実態は、以下のような文字列です。
eyJ0eXAiOiJKV1QiLCJhbGci0iJSUzI1NiJ9.eyJzdWIi0iJleGFtcGxlLXVzZXItaWQiLCJleHAiOjE
3NjY3MTU1MjR9.JE8ftJplm9IwgkCuafNk1-StuKKu8nyHfDRvSzFirJ-uvcGz6TduMwB1MQepL56ayo
uUShSRkOTnnIyHR21DHtu9qaxGzCSi710kdX6uYZh0D8XHkBrft7csvGHqZqoZrFIVxpiFC7F5h0aLMa
QqQmuPmu5pOiKplFMcq_LJQN6t-yfFZdtiZ30ZVInl64QY6JtH4wvqUwAALZkRccyybhxmauQWPNx6UE
spId1XIjR_b4sx4QWXneJPFlE9k9pwSuduD_9SNQJYfnmjvaLYG4IHUMnOAHse2pyVxt3O9stmyQQTkr
J1ai9HqSOQnk33N_97K_Y8JXrF25CyW3SDXw
主な利用目的
JWTは主に、Webサービスにおける認証や認可のためのトークンとして利用されます。
電子署名が含まれているため、もしJWTが改竄されても、サーバー側で検知することができます。
具体的な利用方法
JWTは、HTTPリクエストのヘッダー(Authorization ヘッダー)に含めて送信するのが一般的です。
HTTPリクエストにJWTを含めると、例として以下のようになります。
POST /api/sample HTTP/1.1
Host: sample.com
Authorization: Bearer eyJ************************************************...
Content-Type: application/json; charset=UTF-8
Accept: application/json
{
"content": "送信する内容です。"
}
上記のように、 Authorization: Bearer <JWTトークン> とヘッダーに書かれて送信されます。
JWTの構成
JWTは、「ヘッダー」「ペイロード」「署名」の3つの部分からなります。
それぞれがBase64URLでエンコードされた文字列で、以下の3つはドット . で区切られています。
ヘッダー
ヘッダーをデコードすると以下のようになります。
{
"alg": "RS256",
"typ": "JWT"
}
"alg" として、署名に利用した鍵のアルゴリズムが書かれています。
"RS256" はRSA-SHA256(公開鍵暗号方式)、"HS256" ならHMAC-SHA256(共通鍵暗号方式)です。
"typ" は常に "JWT" です。
ペイロード
ペイロードは、JWTの情報本体です。デコードすると、例えば以下のようになります。
{
"sub": "U000123",
"name": "山田 太郎",
"iat": 1766715524,
"exp": 1766801924
}
いくつかは予約語で、以下はよく使われます。
"sub" :ユーザーを表すIDなど
"iat" :トークンの発行日時(Unixtime)
"exp" :トークンの失効日時(Unixtime)
また、そのほかに、ペイロードには自由な値を入れることができます。(ここでは"name")
署名
署名は、ヘッダーとペイロードを、(RSAなら秘密鍵で)暗号化したもので、デコードできません。
ヘッダーとペイロードが改竄されていないことを保証するための文字列で、
検証の際は鍵を(RSAなら公開鍵を)使用することになります。
実装
お待たせしました。JWTを発行するプログラムを実装していきます。
今回はPHPを利用します。
使用ライブラリー
firebase/php-jwt を利用します。
3条項BSDライセンス(オープンソース)なのでありがたいですね。
事前準備
- PHPのインストール + Pathを通す
- Composerのインストール
- 依存関係のダウンロード
- php-jwtのダウンロード
-
composer require firebase/php-jwt
-
- libsodium(暗号化ライブラリー)のダウンロード
-
composer require paragonie/sodium_compat
-
- php-jwtのダウンロード
鍵の生成
鍵の発行
今回はRSA署名(OpenSSL)を使用します。
シェルで以下のコマンドを実行すると、
秘密鍵 server.key と 公開鍵 pub.key が生成されます。
しっかり管理しましょう。
openssl genrsa 2048 > server.key
openssl rsa -pubout < server.key > pub.key
鍵の保存
生成された鍵をPHPで使えるように、別ファイル上に定義します。
今回は簡易的に別ファイルに定義しますが、
よりセキュアにするには、
環境変数に入れる、外部ファイルで読み込む、などして管理してください。
<?php
const PUBLIC_KEY = '-----BEGIN PUBLIC KEY-----
MII*************************************************************
*************************** 中略 *******************************
****************************************************************
*****QAB
-----END PUBLIC KEY-----';
const PRIVATE_KEY = '-----BEGIN PRIVATE KEY-----
MII*************************************************************
*************************** 中略 *******************************
****************************************************************
*********************T0=
-----END PRIVATE KEY-----';
-----BEGIN PRIVATE KEY----- などは必ずつけたままにしてください。
JWTエンコーダー/デコーダーの実装
いよいよJWT発行プログラムの実装です。
公式サイト のサンプルを見つつ、実装してみます。
実装コード
<?php
require_once '/path/to/vendor/autoload.php';
require_once '/path/to/config.php';
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
$payload = [
'sub' => 'example-user-id',
'exp' => time() + (24 * 60 * 60) // 24時間後
];
$jwt = JWT::encode($payload, PRIVATE_KEY, 'RS256');
echo "JWT: \n" . print_r($jwt, true) . "\n";
echo "\n";
$decoded = JWT::decode($jwt, new Key(PUBLIC_KEY, 'RS256'));
echo "decoded: \n" . print_r($decoded, true) . "\n";
これだけです。コード自体は簡単ですね。
実行結果
これを実行すると以下のような結果を得ます。
$ php sample.php
JWT:
eyJ0eXAiOiJKV1QiLCJhbGci0iJSUzI1NiJ9.eyJzdWIi0iJleGFtcGxlLXVzZXItaWQiLCJleHAiOjE
3NjY3MTU1MjR9.JE8ftJplm9IwgkCuafNk1-StuKKu8nyHfDRvSzFirJ-uvcGz6TduMwB1MQepL56ayo
uUShSRkOTnnIyHR21DHtu9qaxGzCSi710kdX6uYZh0D8XHkBrft7csvGHqZqoZrFIVxpiFC7F5h0aLMa
QqQmuPmu5pOiKplFMcq_LJQN6t-yfFZdtiZ30ZVInl64QY6JtH4wvqUwAALZkRccyybhxmauQWPNx6UE
spId1XIjR_b4sx4QWXneJPFlE9k9pwSuduD_9SNQJYfnmjvaLYG4IHUMnOAHse2pyVxt3O9stmyQQTkr
J1ai9HqSOQnk33N_97K_Y8JXrF25CyW3SDXw
decoded:
stdClass Object
(
[sub] => example-user-id
[exp] => 1766715524
)
無事に、JWTを発行することができました。
余談
以下のサイトで、JWTの検証を行うことができます。
発行されたJWTと鍵を貼り付けると、デコードしてくれます。妥当性の確認に使えます。
注意
ここに出てくるコードは、あくまで簡易的な学習用サンプルとしてお考え下さい。
運用する際は、セキュリティー面をしっかり考えましょう。
おわりに
JWTを実装する日本語の記事は少なめでしたので、まとめました。
間違いなどありましたら、コメントなどでお教えください。修正いたします。
この記事が皆さんの学習の助けになると嬉しいです。