JWTの構造理解と、認可(権限チェック)の基本的な考え方
ファイルアップロード/ダウンロード機能を持つアプリケーションにおいて、機密性の高いデータの漏洩や不正なコード実行 (RCE) を防ぐための必須事項をまとめたものです。
JWT(JSON Web Token)は、ドット (.) で区切られた3つの要素から成り立っています。
以下の3つの要素は、それぞれ異なる役割を持っています。
1. Header (ヘッダー)
・役割:トークンの検証方法の指示
・目的と意味:「このトークンを検証する際、どのアルゴリズムを使うか」 というメタ情報(alg)を指定します。
2. Payload (ペイロード)
・役割:ユーザーIDと認可情報 (Role/Scope) を格納
・目的と意味:「このトークンが誰のもので、何ができるか」 という実際の認証・認可に必要なデータを運びます。これにより、サーバーはデータベースを参照せずにユーザーを識別できます。
・注意点:機密情報(パスワードや個人情報など)は絶対に格納しないでください。 PayloadはBase64でエンコードされているだけであり、誰でも簡単にデコードして中身を見られるためです(見られても問題ない情報のみ格納します)。
3. Signature (署名)
・役割:トークンの改ざん防止
・目的と意味:「このトークンが、発行したサーバーから出たオリジナルのものであり、途中で中身が変わっていないか」 を保証します。これがJWTのセキュリティの核です。
・注意点:リクエストを受け取るたびに、必ず署名を検証してください。署名検証が失敗した場合、HeaderまたはPayloadが改ざんされた証拠であるため、トークンを拒否します。
補足:exp (Expires)
・役割:exp はJWTのPayload内に格納される情報です。
・目的と意味
「このトークンがいつまで有効か」 を示します。期限切れのトークンは、たとえ署名が正しくても無効と判断されます。
認可(権限チェック)の基本的な考え方
認可 (Authorization) とは、「認証済みのユーザーが、特定のリソース(ファイル、APIエンドポイントなど)にアクセスしたり操作したりする権限を持っているか」を確認する処理である
・認可情報の利用(Payloadからの抽出)
・JWTベースのシステムでは、認可に必要な情報はすべてPayloadに含まれています。
・Payloadは、JSON形式で記述された情報格納する場所です。
・ロール (Role):ユーザーの役割(例: admin, user)
・スコープ (Scope): 許可されている操作の範囲(例: read:files, write:files)
権限チェックの実行(認可ロジック)
サーバーは、リクエストされたAPIエンドポイント(操作)に対し、JWTから抽出した情報が許可条件を満たしているかを確認します。
-
リクエストの解析: ユーザーはどのエンドポイント(操作)にアクセスしようとしているか?(例: DELETE /api/v1/files/{id})
-
権限の確認: この操作には、Payloadの権限(例: ロールが admin)が必要か?
-
判定: 権限を満たしていればアクセスを許可し、満たしていなければ処理を拒否し、403 Forbidden を返します。
認可のパターン:RBAC vs ABAC
| パターン | 略称 | 考え方 | 適用例(ファイル管理) |
|---|---|---|---|
| ロールベースアクセス制御 | RBAC (Role-Based Access Control) | 役割(ロール) に基づいて静的に権限を決定 | Manager ロールは全ユーザーのファイルを読めるが、User ロールは自分のファイルのみアクセス可能 |
| 属性ベースアクセス制御 | ABAC (Attribute-Based Access Control) | ユーザー、リソース、環境の複数の属性を組み合わせて動的に権限を決定 | ユーザーの部署が HR であり、かつリクエスト時刻が平日の場合にのみ、人事ファイルへのアクセスを許可 |
・注意点:有効期限を短く設定し、セキュリティリスクを低減します。仮にトークンが漏洩しても、すぐに使えなくなるためです。期限切れトークンは必ず拒否してください。
認証・認可とファイル管理
・前回、基礎(ご提示の内容): 仕組みと動作原理の理解
認証・認可がどのように機能しているかという基礎的な部分に触れました。
・JWTの構造理解: トークンが「ヘッダー」「ペイロード」「署名」で構成されていること、署名が改ざんを防ぐ核であることなど。
・認可の基本: ロール(RBAC)や属性(ABAC)を使って、権限をチェックする基本的なロジック。
これらは、「正しく動く認証システム」 を作るための土台です。
認証・認可に関する高度なトピック:3つの深掘り
基礎的な認証・認可の仕組み(ユーザーIDとパスワードの送信、セッションの利用など)を理解した上で、より堅牢なシステムを構築するための実践的な対策を3つの柱について。
1. APIキー/トークン管理とセキュリティ
一般的な認証で使われるトークン(アクセストークンやリフレッシュトークン)を、どう安全に生成・運用・破棄するかというテーマです。
| トークン種別 | 役割 | ベストプラクティス |
|---|---|---|
| アクセストークン | 実際のAPIへのアクセス許可証 | 有効期限を短く設定する(例: 5分~1時間)。漏洩しても被害を最小限に抑えるため |
| リフレッシュトークン | アクセストークンを再発行するための鍵 | 厳重に保管し、有効期限を長く設定する(例: 数日~数ヶ月) |
| APIキー | サービス間連携や外部開発者向けの鍵 | 利用範囲を限定(特定のIPアドレス、特定のAPI)し、定期的に更新する |
リフレッシュトークンローテーション(最重要)
リフレッシュトークンが盗まれた場合、攻撃者はこれを使って新しいアクセストークンを無限に発行できてしまいます。これを防ぐために「ローテーション」を導入します。
仕組み: リフレッシュトークンを使って新しいアクセストークンを発行する際、古いリフレッシュトークンを即座に無効化し、新しいリフレッシュトークンも一緒に発行してクライアントに返します。
効果:盗まれたリフレッシュトークンは一度使われると無効になるため、不正利用できる時間が極めて短くなります。
トークンの不正利用検知(Revocation / Invalidation)
アクセストークンやリフレッシュトークンは、有効期限が切れるまで有効ですが、ユーザーがログアウトしたり、パスワードを変更したりした場合は、有効期限前でも 強制的に無効化(Revoke) する必要があります。
実装:バックエンドでトークンIDのリスト(ブラックリスト)を保持し、リクエストが来るたびにそのトークンが破棄済みでないか確認する仕組みが必要です。
2. 認証情報の安全な保管と利用
ユーザーのパスワードや、バックエンドが使う重要な秘密情報(シークレット)の安全な取り扱い方です。
パスワードのハッシュ化(bcrypt/Argon2)とストレッチング
パスワードは絶対にそのままデータベース保存してはいけません。ハッシュ化する際も、一般的なMD5やSHA-256では不十分です。
鍵導出関数を使う: bcryptやArgon2などの鍵導出関数を使用します。これらは通常のハッシュ関数とは異なり、意図的に計算に時間とコストをかけるように設計されています。
ストレッチング(コスト調整): 計算コストを調整するパラメータ(ラウンド数など)を設定します。これにより、攻撃者が「パスワードリスト」を使って総当たり攻撃(ブルートフォース)を仕掛けても、非常に時間がかかるようにします。
なぜ計算を重くするのか?
攻撃者はGPUなどを使って超高速でハッシュ計算を試みます。計算を重くすることで、1秒間に試せる回数が劇的に減り、攻撃の費用対効果を下げることができます。
バックエンドサービス間でのシークレット管理
バックエンドのアプリケーションコードの中に、データベースの接続情報や外部APIキーなどの秘密情報(シークレット)を直接書き込むのは厳禁です。
仕組み:AWS Secrets Manager、HashiCorp Vaultなどの専用ツールを導入し、秘密情報を一元管理します。
利用:アプリケーションは、起動時にこれらのシークレット管理ツールから必要な情報を取りにいくように、コードと秘密情報を完全に分離します。
3. レートリミットとブルートフォース攻撃対策
悪意のあるユーザーがAPIを高速で叩き続けたり、ログイン試行を繰り返したりするのを防ぐための防御策。
レートリミット(時間あたりのリクエスト制限)の実装
特定のAPIやログインエンドポイントに対し、一定時間内に受け付けるリクエストの回数を制限します。
制限の種類:IPアドレスベース: 1つのIPアドレスから1分間に100回まで、など。
ユーザーIDベース: ログイン後のユーザーからのAPI呼び出しを制限。
エンドポイント別::ログインエンドポイントは厳しく(例: 1分に5回)、情報取得APIは緩く、など。
実装場所:ロードバランサー(AWS ALBなど)、API Gateway、またはアプリケーションのミドルウェア層(Redisなどでリクエスト数をカウント)で実装が必須。
ブルートフォース攻撃対策(ログイン)
ログインエンドポイントへの総当たり攻撃は、以下を組み合わせて防ぎます。
レートリミット:前述の通り、短時間で大量に試行させない。
アカウントロックアウト:パスワードを数回連続で間違えた場合、一定時間(例: 30分)そのアカウントでのログインを一時的に禁止する。
キャプチャ(CAPTCHA):連続失敗時や高頻度アクセス時に、人間であることの証明(私はロボットではありません)を要求する。
これらの対策を組み合わせることで、システムの安定性を保ち、不正アクセスからユーザーアカウントを守ることができます。