Closed3

JWTについて理解する

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

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ヘッダーに毎回送ると負荷が増す可能性あり
OsamuOsamu

JWTの構成

[ヘッダ].
[ペイロード].
[署名]
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
t42p4AHef69Tyyi88U6+p0utZYYrg7mmCGhoAd7Zffs

https://qiita.com/knaot0/items/8427918564400968bd2b

役割

ヘッダー

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ヶ月前にクローズされました