JWTの利点 ‐ 認証システム技術解説 - nyg1971/business_api GitHub Wiki
JWTの利点 - 認証システム技術解説
📋 概要
JWT(JSON Web Token)の技術的利点と、従来のセッション認証との比較を実装例と共に解説します。
🔄 ステートレス vs ステートフル認証
ステートフル認証(従来型)
# Railsの標準的なセッション認証
class SessionsController < ApplicationController
def create
user = User.find_by(email: params[:email])
if user&.authenticate(params[:password])
session[:user_id] = user.id # ← サーバー側でセッション保存
redirect_to dashboard_path
end
end
end
# サーバー側でセッションデータを保存・管理
# Redis、メモリ、DBなどにセッション情報を保持
ステートレス認証(JWT)
# 今回実装したJWT認証
def login
if user&.valid_password?(params[:password])
token = JsonWebToken.encode(user_id: user.id) # ← トークンに情報を含める
render json: { token: token } # ← サーバーは何も保存しない
end
end
# サーバー側では何も保存せず、トークン自体に情報を含める
🎯 JWTがステートレスな部分
# 認証時:サーバーは何も覚えない
def authenticate_request
token = header.split(' ').last
decoded = JsonWebToken.decode(token) # ← トークンから情報を取得
@current_user = User.find(decoded[:user_id])
# サーバー側にセッション情報なし、全てトークンから復元
end
🔐 JWTの検証メカニズム
1. このサーバーで生成されたもの
# トークン生成時
SECRET_KEY = Rails.application.credentials.secret_key_base
token = JWT.encode(payload, SECRET_KEY)
# ↓
# SECRET_KEYで「デジタル署名」を作成
# トークン検証時
JWT.decode(token, SECRET_KEY)
# ↓
# 同じSECRET_KEYで署名を検証
# 署名が一致 = このサーバーで生成されたことを証明
# 署名が不一致 = 他のサーバーまたは改ざんされたトークン
2. 24時間以内に発行したもの
# トークン生成時
def self.encode(payload, exp = 24.hours.from_now)
payload[:exp] = exp.to_i # ← Unix時間で有効期限をセット
JWT.encode(payload, SECRET_KEY)
end
# トークン検証時
JWT.decode(token, SECRET_KEY)
# ↓
# jwt gem が自動的に exp をチェック
# 現在時刻 > exp なら JWT::ExpiredSignature エラー
🔐 セキュリティの仕組み
署名による改ざん検出
# 正規トークンの構造
# header.payload.signature
# eyJhbG... ← ヘッダー(暗号化アルゴリズム情報)
# eyJ1c2... ← ペイロード(ユーザーID、有効期限等)
# TNV6UX... ← 署名(SECRET_KEYで生成)
# 悪意のある改ざん例
# ペイロードのuser_idを変更 → 署名が一致しない → エラー
# 有効期限を延長 → 署名が一致しない → エラー
SECRET_KEYの重要性
# 攻撃者が SECRET_KEY を知らない限り
# 1. 有効な署名を作成できない
# 2. 既存トークンを改ざんできない
# 3. 新しい有効なトークンを偽造できない
💡 実際の検証フロー
def authenticate_request
begin
decoded = JsonWebToken.decode(token)
# ↓ この時点で以下を自動チェック
# ✅ 署名が正しい(このサーバーで生成)
# ✅ 有効期限内(24時間以内)
# ✅ トークン形式が正しい
@current_user = User.find(decoded[:user_id])
rescue JWT::DecodeError => e
# 署名不正、期限切れ、形式エラー等をキャッチ
render_unauthorized
end
end
🚀 JWTの主要な利点
1. 改ざん検出
- ペイロードの1文字でも変更されると署名が無効
- 攻撃者は有効なトークンを偽造できない
2. 有効期限管理
- トークン自体に期限情報を含む
- サーバー側で期限管理の必要なし
3. ステートレス
- サーバーはトークンの状態を保存不要
- スケーラビリティが高い
4. 分散システム対応
# 複数のAPIサーバーがあっても
# どのサーバーでも同じSECRET_KEYがあれば認証可能
server_1.authenticate(token) # ✅ 認証成功
server_2.authenticate(token) # ✅ 認証成功(セッション共有不要)
5. モバイルアプリ・SPA対応
// フロントエンド(React/Vue.js/モバイルアプリ)で
// トークンを保存・送信するだけ
localStorage.setItem('token', response.token);
// API呼び出し時
fetch('/api/v1/me', {
headers: {
'Authorization': `Bearer ${token}`
}
})
🔄 従来のセッション認証との比較
セッション認証の問題点
# サーバー側で状態を保持する必要がある
session_store = {
"sess_123abc" => { user_id: 1, login_time: "2025-06-18 10:00" },
"sess_456def" => { user_id: 2, login_time: "2025-06-18 11:00" },
# ... 全ユーザーのセッション情報をサーバーが記憶
}
# ユーザーがアクセスするたびに
# 1. セッションIDを受け取る
# 2. session_store から情報を検索
# 3. セッション有効期限をチェック
# 4. ユーザー情報を取得
問題点:
- メモリ使用量が多い(全ユーザー分のセッション情報)
- サーバー間でのセッション共有が複雑
- セッション情報の管理(期限切れ削除等)が必要
JWTの解決策
# JWT方式:サーバーは何も記憶しない
def authenticate_request
# トークンから全ての情報を復元
decoded = JsonWebToken.decode(token) # メモリ不要
@current_user = User.find(decoded[:user_id])
# セッション管理不要、期限切れは自動判定
end
利点:
- メモリ使用量が少ない
- サーバー間でのデータ共有不要
- セッション管理の運用コスト削減
🎯 実装における注意点
トークンの取り扱い
# 安全なトークン生成
class JsonWebToken
SECRET_KEY = Rails.application.credentials.secret_key_base # 秘密鍵
def self.encode(payload, exp = 24.hours.from_now)
payload[:exp] = exp.to_i
JWT.encode(payload, SECRET_KEY)
end
end
エラーハンドリング
# 適切なエラー処理
begin
decoded = JsonWebToken.decode(token)
rescue JWT::ExpiredSignature
render json: { error: 'Token has expired' }, status: :unauthorized
rescue JWT::DecodeError
render json: { error: 'Invalid token' }, status: :unauthorized
end
💡 学習・習得のポイント
技術的理解
- ステートレス認証の概念とメリットの理解
- セキュリティ(署名検証・期限管理)の仕組みの把握
- 従来方式との違いとトレードオフの理解
実装スキル
- JWT gemの適切な使用方法の習得
- Rails APIでの認証フローの実装
- フロントエンド(React/SPA)との連携を考慮した設計
実務への応用
- スケーラビリティを考慮した認証システム設計
- モバイルアプリ・多言語対応への拡張性の理解
- セキュリティリスクの理解と対策の実装
JWTは現代のAPI中心の開発では必須の技術です。ステートレスという特性により、マイクロサービス、SPA、モバイルアプリなど、あらゆる現代的な開発スタイルに対応できる重要な認証手法となっています。