認証・認可設計で失敗しないために|セッションとJWTの使い分けからリフレッシュトークンまで

認証まわりの設計は、一度リリースすると後から方式を変えるのが非常に難しい領域です。「とりあえずJWTで」と流行に乗って導入した結果、ログアウトやアカウント停止を即時反映できずに苦労する——という相談は今でもよく受けます。

この記事では、セッション方式とJWT方式の本質的な違い、トークンの保存場所、有効期限とリフレッシュの設計、そして認可ロジックをどこに置くかという4つの論点を整理します。

目次

セッション方式とJWT方式の違いは「状態を持つ場所」

両者の違いは、認証状態をサーバー側に持つか、トークン自体に埋め込むかです。

  • セッション方式: サーバー側(Redis等)に状態を保存し、クライアントにはセッションIDだけ渡す。即時無効化が容易で、漏洩時もサーバー側で破棄できる。毎リクエストでストアへの参照が発生
  • JWT方式: 署名付きトークンに情報を埋め込み、サーバーは署名検証だけで認証できる。ストア参照が不要でサービス間連携に向くが、発行済みトークンを即時無効化できないのが本質的な弱点

実務の目安として、単一のWebアプリならセッション方式で十分なことが多く、複数サービスで認証を共有する構成や外部APIの認可ではJWT(OAuth 2.0 / OpenID Connect)が適します。「JWTのほうがモダン」という理由だけで選ぶのは避けるべきです。無効化の要件(強制ログアウト、アカウント凍結の即時反映)があるなら、JWTでもブロックリスト等の状態管理が結局必要になり、セッション方式との差が縮まります。

トークンの保存場所——localStorageは避ける

ブラウザでのトークン保存場所は、XSSへの耐性で選びます。

  • localStorage: JavaScriptから読めるため、XSSが1箇所あればトークンごと盗まれる。長期トークンの置き場所には不向き
  • Cookie(HttpOnly + Secure + SameSite): スクリプトから読めず、XSSでの窃取を防げる。CSRFはSameSite属性とCSRFトークンで対策する

SPAでもリフレッシュトークンはHttpOnly Cookieに置き、アクセストークンはメモリ上でのみ保持するのが現在の定石です。「SPAだからlocalStorage」と機械的に決めているコードを見かけたら、見直しをおすすめします。

有効期限とリフレッシュトークンの設計

「即時無効化できない」JWTの弱点は、有効期限を短くすることで緩和します。基本形は次の2段構成です。

  • アクセストークン: 短命(15分〜1時間程度)。漏洩しても被害時間を限定する
  • リフレッシュトークン: 長命(数日〜数週間)。サーバー側で管理し、失効させられるようにしておく

リフレッシュトークンをサーバー側で管理していれば、強制ログアウトは「リフレッシュトークンの失効」で実現でき、影響はアクセストークンの残り寿命だけに抑えられます。さらにリフレッシュのたびにトークンを使い捨てで再発行するローテーション方式にすると、盗まれた古いトークンの再利用を検知できます。

認可の実装場所を一箇所に集める

認証(誰か)より事故が多いのは認可(何をしてよいか)です。特に「他人のリソースIDを指定したら見えてしまう」IDOR系の脆弱性は、認可チェックが各コントローラに散らばっていて漏れることで生まれます。

  • ロール単位の粗い制御(管理者のみ等)は、ミドルウェアやフィルタ層に集約する
  • リソース単位の所有チェックは、コントローラではなくサービス層・リポジトリ層で強制する(クエリ条件に必ず所有者IDを含める等)
  • フロントエンドでの出し分けはUXのためのもので、認可はサーバー側で必ず再検証する
-- 「取得してからチェック」ではなく、クエリ自体に認可を織り込む
SELECT * FROM documents
 WHERE id = ? AND owner_id = ?;  -- 0件なら404を返す

まとめ

認証・認可の設計は、方式の新しさではなく要件——特に「無効化をどれだけ即時に反映したいか」——から逆算するのが失敗しないコツです。単一アプリならセッション方式を第一候補に、JWTを使うなら短命アクセストークン+管理されたリフレッシュトークンの2段構成に。トークンはHttpOnly Cookieを基本とし、認可チェックは層を決めて一箇所に集約する。この原則を押さえておけば、後から取り返しのつかない設計を避けられます。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

クラウド・バックエンドエンジニア。AWSを中心に設計・構築から運用までを担当しています。主要言語は Java・JavaScript・Python。運用の現場で拾った知見を、再現できる手順に落として残すのがこのブログのテーマです。

目次