Closed3
JWTについて理解する

- JWTについて理解する
- 他の人に説明できる状態がゴール

JWTの概要
- JSON Web Tokenの略
- Token(暗号化された文字列)にてユーザーの認証を行う
- 秘密鍵を用いて諸々の認証情報をTokenにする(複合する時も同じ鍵を使う)
- トークンベースでの認証
こちらの動画もとてもわかりやすかったです↓↓
なぜJWTを使うのか?
- サーバー側にセッション情報を持たなくていい
- トークン内に全て情報があるのでJWTが自己完結できる
- トークンベースでの認証はAPIとも相性がいい
- JWTのpayloadに柔軟な情報を入れることができるため
- 多くの言語で利用できる
ユースケース↓↓
ケース | JWTを使うべきかどうか | 理由 |
---|---|---|
SPAやモバイルアプリのAPI認証 | ✅ 使うべき | クッキーに依存せず、ステートレスでAPIと連携しやすい |
マイクロサービス間の通信認証 | ✅ 使うべき | トークン一つで認証情報を安全にやり取りできる |
OAuth / OpenID Connect | ✅ 使うべき | 標準仕様でJWT形式のIDトークン・アクセストークンが用いられる |
サーバーレス環境(例: AWS Lambda) | ✅ 使うべき | ステートレスな環境でユーザー情報を保持・検証できる |
複数ドメイン間の認証連携(SSOなど) | ✅ 使うべき | JWTを使えばトークンベースでドメインを跨いで認証情報を共有可能 |
セッションの即時無効化が必要(ログアウト、強制ログアウト) | ❌ 避けるべき | JWTは基本的に一度発行するとサーバーで取り消せない(ステートレスだから) |
センシティブな情報(個人情報、クレジット情報など)を扱う | ❌ 避けるべき | JWTはデフォルトで暗号化されておらず、中身は誰でも見える(署名は改ざん防止用) |
通常のWebアプリでクッキーによるセッション管理で十分 | ❌ 避けるべき | クッキー+サーバーセッションの方が簡単かつ安全に実装可能 |
トークンのサイズが重要(通信量がシビアな環境) | ❌ 避けるべき | JWTは比較的サイズが大きく、HTTPヘッダーに毎回送ると負荷が増す可能性あり |

JWTの構成
[ヘッダ].
[ペイロード].
[署名]
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
t42p4AHef69Tyyi88U6+p0utZYYrg7mmCGhoAd7Zffs
役割
ヘッダー
JWTの署名検証を行う方法を記載するパートで、署名のアルゴリズムやトークンの種類などを記載します。
下記のようなJSONデータをbase64エンコードすることで、ヘッダー情報が生成されます。
{
"alg": "RS256", => 署名のアルゴリズム
"kid": "52fa0f122222gadg04e59axxxxxxxxx", => 署名に仕様するキー
"typ": "JWT" => トークンの種類
}
ペイロード
JWTの本体部分になるところで、やり取りに必要な属性情報(claim)を記載します。このパートはアプリケーション側で任意の値を埋め込むことができます。こちらもJSONをbase64エンコードして、ペイロード情報を生成します。
下記は、よく使われる値の一例です。
{
"sub": "453665722", => 認証の対象となるユーザの識別子
"name": "John Doe", => ユーザーの名前
"iat": 1516239022 => トークンの発行日時を表すタイムスタンプ(issued at)
"exp": 1660964863, => トークンの有効期限を表すタイムスタンプ(expired at)
"aud": "xxxxxxxx", => トークンが利用されるべきクライアント(受信者)識別子(audience)
"iss": "https://xxxxxxxxx", => トークンの発行者を表す識別子 (issuer)
}
署名
署名のパートは、トークン作成者の本人証明をするパートです。
下記のように既にエンコード済みヘッダー、ピリオド(".")、エンコード済みペイロードがあります。
<エンコード済みのヘッダー>.<エンコード済みのペイロード>
これらを連結したものを入力値として、本人の「秘密鍵」と「最初に定義した署名アルゴリズム」で計算することで生成されます。
今回最初に確認したJWTでは、下記のような計算になりますね。
受信側では公開鍵を使って、検証することで改ざんや偽造が行われていないことを確認します。
Base64URLSafe(Sign('HS256', '${PRIVATE_KEY}',
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ'))) ->
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
この最後の署名を秘密鍵で検証して問題なければok、という流れ
このスクラップは4ヶ月前にクローズされました