2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JWT発行について実装しながら勉強した

Posted at

はじめに

認証について調べていると、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
        

鍵の生成

鍵の発行

今回はRSA署名(OpenSSL)を使用します。

シェルで以下のコマンドを実行すると、
秘密鍵 server.key と 公開鍵 pub.key が生成されます。
しっかり管理しましょう。

openssl genrsa 2048 > server.key
openssl rsa -pubout < server.key > pub.key

鍵の保存

生成された鍵をPHPで使えるように、別ファイル上に定義します。
今回は簡易的に別ファイルに定義しますが、
よりセキュアにするには、
環境変数に入れる、外部ファイルで読み込む、などして管理してください。

config.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発行プログラムの実装です。
公式サイト のサンプルを見つつ、実装してみます。

実装コード

sample.php
<?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を実装する日本語の記事は少なめでしたので、まとめました。

間違いなどありましたら、コメントなどでお教えください。修正いたします。

この記事が皆さんの学習の助けになると嬉しいです。

  1. RFC7519 で、推奨発音に関する記述あり。

2
0
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
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?