1. 前提知識:暗号化と署名の違い
暗号化(Encryption)の目的と仕組み
暗号化の特徴:
- データを第三者に読まれないよう秘匿することが目的
- 適切な復号化キーを持たない者はデータの内容を読めない
- データの機密性を保護
デジタル署名(Digital Signature)の目的と仕組み
デジタル署名の特徴:
- データの完全性(改ざんされていないこと)を保証
- 認証(誰が作成したかの証明)を提供
- データ自体は暗号化されず、誰でも読める
2. JWTの構造と動作原理
JWTの構造
JWTの実際の形式
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
各部分の説明:
- Header(赤色部分): Base64エンコードされたヘッダー情報
- Payload(青色部分): Base64エンコードされたペイロード(暗号化されていない)
- Signature(緑色部分): Header + Payloadの署名
重要なポイント:ペイロードは暗号化されていない
3. 現在の実装における問題点
間違った使用例:機密データをJWTに含める
問題点:
- 機密情報がBase64エンコードされただけでクライアントに送信
- 悪意のある第三者が容易に内容を読み取り可能
- JWTの設計思想と異なる用途での使用
セキュリティリスクの可視化
4. 正しいJWTの使用方法
認証トークンとしての適切な使用
JWTに含めるべき情報 vs 含めるべきでない情報
5. 機密データの適切な取り扱い方法
アーキテクチャパターンの比較
現在の問題のあるパターン
推奨パターン1:サーバーサイド管理
推奨パターン2:適切な暗号化
Ruby on Railsでの実装例
現在の問題のあるコード例
# ❌ 問題のある実装
class AuthenticationController < ApplicationController
def login
user = User.find_by(email: params[:email])
if user&.authenticate(params[:password])
# 機密情報もJWTに含めている
payload = {
user_id: user.id,
email: user.email,
credit_card_number: user.credit_card_number, # ❌ 機密情報
api_key: user.api_key, # ❌ 機密情報
internal_notes: user.internal_notes # ❌ 機密情報
}
token = JWT.encode(payload, Rails.application.secret_key_base)
render json: { token: token }
end
end
end
推奨される実装
# ✅ 推奨される実装
class AuthenticationController < ApplicationController
def login
user = User.find_by(email: params[:email])
if user&.authenticate(params[:password])
# 公開しても問題ない情報のみJWTに含める
payload = {
user_id: user.id,
role: user.role,
exp: 24.hours.from_now.to_i,
iat: Time.now.to_i
}
token = JWT.encode(payload, Rails.application.secret_key_base)
# 機密情報はサーバーサイドで管理
user.update(last_login_at: Time.current)
render json: {
token: token,
user: {
id: user.id,
name: user.name,
role: user.role
}
}
end
end
end
# 機密データが必要な場合は別途APIで取得
class UserDataController < ApplicationController
before_action :authenticate_user!
def show
# JWTから取得したuser_idを使用して機密データを取得
user_data = current_user.secure_data
# 必要に応じて暗号化して送信
encrypted_data = encrypt_sensitive_data(user_data)
render json: { data: encrypted_data }
end
private
def encrypt_sensitive_data(data)
# AESなどの適切な暗号化ライブラリを使用
cipher = OpenSSL::Cipher.new('AES-256-CBC')
cipher.encrypt
cipher.key = Rails.application.credentials.encryption_key
cipher.iv = iv = cipher.random_iv
encrypted = cipher.update(data.to_json) + cipher.final
Base64.encode64(encrypted)
end
end
7. まとめ
重要なポイント
結論: JWTは認証のためのツールであり、暗号化のためのツールではありません。機密データの保護には適切な暗号化ライブラリと設計パターンを使用し、JWTは本来の目的である認証と完全性保証に使用することが重要です。