ssecutils
Security / Browser-native guide

セッション認証とJWT認証の違い

保存場所、失効、リフレッシュトークン、用途別の選び方を比較します。

7Zero tracking reading surface

はじめに: 「ログイン状態」の保ち方

HTTP はステートレスなプロトコルで、リクエストごとに「あなたは誰なのか」をサーバ側に思い出させる仕組みが必要です。これを実現する代表的な方式が2つ:

  • セッション認証(伝統的、サーバ状態あり)
  • JWT 認証(モダン、ステートレス)

この記事では「結局どっちがいいの?」という問いに、シーン別の答えを示します。結論を先に書くと:「Webサービスのログインなら原則セッション、API/SPA/モバイルとの組み合わせで JWT、組織横断SSOなら OIDC」 ──ですが、なぜそうなるのかを順を追って整理します。

方式1: セッション認証

1990年代から続く伝統的な方式。仕組みは単純:

  1. ユーザーがログイン → サーバはランダムなセッションIDを発行
  2. サーバ側 DB / Redis に session_id → user_id のマッピングを保存
  3. クライアントには Set-Cookie でセッション ID を渡す
  4. 以降のリクエストでは、ブラウザが Cookie を自動付与してサーバに送信
  5. サーバは Cookie のセッション ID で DB を引いて、ユーザーを特定
# ログインレスポンス
HTTP/1.1 200 OK
Set-Cookie: sid=a1b2c3d4e5f6; HttpOnly; Secure; SameSite=Lax

# 以降のリクエスト
GET /api/me HTTP/1.1
Cookie: sid=a1b2c3d4e5f6

セッション ID 自体は意味のないランダム文字列で、サーバ側のセッションストアが「真実の保管場所」です。

セッション認証の利点

  • サーバ側で即時に無効化可能: ログアウト時、不正検知時、セッションストアからレコードを消すだけ
  • 権限変更が即時反映: 「この人を管理者から外す」が次のリクエストで反映
  • クライアント側にユーザー情報を持たない: 漏洩時の被害が限定的
  • 枯れていて実装が安定: フレームワーク標準サポート(Rails / Django / Laravel / Express-session)

セッション認証の欠点

  • サーバ側に状態を持つ: スケールアウトする時、複数サーバ間でセッションストアを共有する必要(Redis / DB)
  • クロスドメインで使いづらい: 別ドメインの API を叩く時、Cookie の SameSite 制約や CORS で詰まる
  • モバイルアプリで Cookie が扱いづらい: WebView 経由でないと自然にいかない

方式2: JWT 認証

2010年代に SPA / モバイルアプリの普及とともに広まった方式。JWT(JSON Web Token)はユーザー情報そのものをトークンに署名付きで詰め込む仕組み。

例えばログインすると、サーバはこんなトークンを返します:

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMiLCJleHAiOjE3MzAwMDAwMDB9.signature

# Base64 デコードすると…
{"alg":"HS256"}.{"sub":"123","exp":1730000000}.{署名}

Payload に sub(ユーザーID)や exp(有効期限)を入れ、サーバの秘密鍵で署名します。サーバは受け取ったトークンの署名を検証するだけでユーザーを認証できる。サーバ側にセッションストア不要がポイント。

# 以降のリクエスト
GET /api/me HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWI...

JWT 認証の利点

  • ステートレス: サーバ側にセッションストア不要、水平スケールが楽
  • マイクロサービス間で共有しやすい: トークン1個で複数サービスが認証可能
  • クロスドメインで使いやすい: Authorization ヘッダ経由で Cookie の制約を受けない
  • モバイル/SPAで自然: ブラウザ依存の Cookie ではなく、アプリのストレージで管理可能
  • ユーザー情報を含められる: 各リクエストで DB を引かなくても sub, role 等が分かる

JWT 認証の欠点

  • 即時失効が困難: 一度発行したトークンは exp まで有効。不正検知して即ログアウトさせたい時にブラックリストが必要 → 結局サーバ状態を持つ
  • 権限変更がリアルタイム反映されない: 「管理者から降格」を実行しても、次のトークン更新まで反映されない
  • ペイロードが大きい: Cookie/ヘッダのサイズが膨らむ(数百バイト〜KB単位)
  • 署名検証の落とし穴が多い: alg=none、アルゴリズム混同、弱い秘密鍵等。詳細は JWT のよくあるセキュリティ問題 参照

保管場所: Cookie か localStorage か

JWT を採用すると次に必ず議論になるのが「ブラウザでどう保管するか」。選択肢は3つあり、それぞれ XSS / CSRF への耐性が違います。

保管場所XSS耐性CSRF耐性送信
localStorage❌ JS から読めるので窃取可✅ 自動送信されないJS で Authorization ヘッダ付与
HttpOnly Cookie✅ JS から読めない❌ 自動送信されるので CSRF 対策必要ブラウザが自動付与
メモリ(変数)△ JS スコープ次第✅ 自動送信されないリロードで消える

現代の推奨は「アクセストークンはメモリ、リフレッシュトークンは HttpOnly Cookie」のハイブリッドパターンが多いです。XSS の影響範囲を「短命なメモリトークンの窃取」に限定でき、長命のリフレッシュトークンは JS から触れない位置に置く。

⚠ 「localStorage に JWT を入れる」はセキュリティ的に最弱。XSS 1個で全ユーザーのトークンが盗まれます。

リフレッシュトークン: 寿命の使い分け

JWT を実運用すると、すぐに「即時失効できない問題」と「短命にすると再ログインが多すぎる問題」のジレンマに直面します。解決策が2種類のトークンを使い分けるパターン:

  • アクセストークン: 短命(5〜15分)、API 呼び出しに使用、漏洩しても影響限定
  • リフレッシュトークン: 長命(数日〜数週間)、アクセストークン更新専用、サーバ側で失効管理

フロー:

  1. ログイン → 両トークンを発行
  2. API 呼び出しはアクセストークンで(普通の JWT 検証のみ、サーバ状態不要)
  3. アクセストークンが期限切れになったら、リフレッシュトークンで新しいアクセストークンを取得
  4. リフレッシュ時にサーバ側で「このリフレッシュトークンが失効していないか」を DB で確認

この設計だと、「99% のリクエストはステートレス、ログイン延長の瞬間だけサーバ状態を読む」という良いとこ取りができます。

結局どちらを選ぶ?

シナリオA: 一般的な Web サービス(SSR ベース)

Rails / Django / Laravel 等のサーバーサイドレンダリングが主体で、フロントエンドが同じドメインで動く構成。

セッション認証。フレームワーク標準、安全、シンプル。JWT を導入するメリットは薄い。

シナリオB: SPA + 別ドメインの API

React/Vue の SPA がフロント、API が別サーバ(別ドメイン)。

JWT または同ドメインに寄せたセッション

  • API を api.example.com、フロントを app.example.com のようにサブドメイン構成にすれば Cookie でセッション認証も可能
  • 完全別ドメイン or マイクロサービス構成なら JWT の方が素直

シナリオC: モバイルアプリ + API

ネイティブアプリと API サーバ。

JWT。Cookie ベースのセッションは扱いづらい。アクセス + リフレッシュトークンで運用。

シナリオD: マイクロサービス / 複数システム

サービス A → サービス B → サービス C と認証を引き継ぐ必要がある構成。

JWT or OIDC。各サービスが独立に署名検証できる JWT が向く。組織横断 SSO なら OpenID Connect (OIDC)(中身は JWT ベース)。

シナリオE: 第三者サービス連携(OAuth)

「Google でログイン」のような第三者連携。

OAuth 2.0 / OIDC。自前で JWT を発行する話とは別軸で、第三者の発行する ID トークン(JWT)を受け取る形。

「JWT がモダンだから JWT」は間違い

2015〜2018年頃の SPA ブームで「セッションは古い、これからは JWT」という空気がありましたが、2026年現在の評価はかなり揺り戻しが起きています:

  • JWT を「使うべきでないところ」で使うとハマるポイントが多すぎる
  • 「JWT in localStorage」の事例ほぼ全てがセキュリティ的にアウト
  • セッション認証 + Cookie の組み合わせが、2026 年の SameSite 既定値・HttpOnly・Secure と相まって極めて堅牢になった

Hacker News や Reddit の議論でも「Stop using JWT for sessions」の論調が定着しています。「ステートレスである必要が本当にあるのか」を最初に問うのが大事。

セキュリティチェックリスト

どちらを選んでも共通でやること:

おわりに

セッション vs JWT は「モダン or 古い」ではなく「使う場所が違う」という関係です。新規プロジェクトでは「ステートレスな必要性が本当にあるか」を最初に考え、必要なければセッションが安全で楽。SPA + API / モバイル / マイクロサービスのような分散構成では JWT が活きます。

本サイトの JWT Decoder で実際の JWT トークンを分解し、署名検証まで実行できます。HTTP Cookie Parser で Set-Cookie の SameSite/HttpOnly 設定をチェックすれば、セッション側の運用も整理できます。

Tool companion

この記事と一緒に使えるツール

Related reading

関連記事