メインコンテンツへ移動

JWTとセッショントークンの違い

セッションは真実をサーバー側に置きます。不透明な16〜32バイトのトークンが、自分で管理するストアを索引し、失効は即座かつ安価です。JWTは真実をトークン自体に運びます――署名済み、ステートレス、どこでも検証可能――しかし失効は有効期限の到来を待つことになります。単一アプリのログインにはセッションを、共有ストアなしに多数のサービスが同一性を検証しなければならない場合にはJWTを使いましょう。

端末内で処理

JWTとセッショントークンの違いとは

どちらの設計も同じ問題を解決します。HTTPリクエストは記憶を持たずに到着するため、各リクエストには「これはログインした同じ人物です」と告げる何かが必要です。古典的なセッションは、クライアントに不透明なトークン――サーバー側の状態を指すランダムな文字列――を渡します。JWT(RFC 7519)は、状態そのものをクライアントに渡します。サーバーが何も照会せずに検証できる、署名済みのクレームの束です。片方は真実をサーバーに保存し、もう片方はそれを公証して携行させます。

この違い――真実がどこに住むか――が、ここにあるすべてのトレードオフに波及します。JWTは署名の検証だけで確認できるため、鍵を持つ任意のサービスが共有ストアなしにリクエストを認証できます。その代償は、トークンがexpクレームまで有効であり続けることで、「今すぐログアウトさせる」も「今すぐこのユーザーを禁止する」も自然な実装がないことです。セッショントークンはストアなしでは無価値なので、失効は削除です――しかし検証するすべてのサービスがそのストアに到達できなければなりません。

どちらかが勝っているわけではなく、成熟したシステムは日常的に両方を運用します――人間向けアプリには不透明なセッションCookie、サービス間には短命のJWT。以下のセクションでは、失効、保存方法、サイズ、攻撃対象領域を比較します。随所にリンクされたデコーダーで、何もアップロードせずに実際のトークンを調べられます。

本番環境のトークンを信頼する前に、JWTデコーダーで1つ展開してみてください――ヘッダーとペイロードをローカルでデコードし、鍵を要求することは一切ありません。

セグメントごとの解剖には、JWT構造ガイドが実際の例とともにヘッダー、ペイロード、署名を順にたどります。

2つの形状、2つの哲学

サーバーセッションはポインタです。トークン――ランダムな16〜32バイトで、通常はCookieとして設定――自体には意味がなく、すべての意味はストアに住んでいます。Redis、データベース、プロセスのメモリもその例です。この間接性こそが機能です。レコードは何でも保持でき、何でも変更でき、消えることもでき、クライアントはポインタしか持っていないため何も気づきません。

JWTは宣言文です。3つのbase64urlセグメント――ヘッダー、ペイロード、署名――がクレームを運びます。ユーザーにはsub、発行者にはiss、有効期限にはexp、加えてカスタムクレームです。署名(共有秘密鍵でのHMAC、または鍵ペアでのRSA/ECDSA)は、宣言が改ざんされておらず鍵の保持者から来たことを証明します。検証に照会は不要です。信頼に必要なのは鍵だけです。

失効:ほとんどのプロジェクトを決めるトレードオフ

セッションなら「サインアウト」も「ユーザーの禁止」もストア操作です。レコードを削除すれば次のリクエストは失敗します。JWTでは、発行サーバーが今どう望もうと、トークンはexpまで暗号学的に有効です――ステートレスである要点は、検証が決して照会し返さないことだからです。緩和策はよく知られており、いずれも状態を再導入します。短い有効期限とリフレッシュトークンのローテーション、jtiの拒否リスト、ログアウト時に繰り上げるバージョンクレーム。それぞれが、手放したストアの一片を再建します。

率直に言えばこうです。セッションは失効を無料にしますが、すべてのリクエストにストア照会を課します。JWTは失効を設計判断に変え、その代金はログアウト時に払います。「すべての場所から即座にログアウト」が要件なら、セッションか、厳格に管理されたローテーションがあなたの未来にあります。

保存方法と攻撃対象領域:Cookie、localStorage、XSS、CSRF

トークンが何であるかと同じくらい、それがどこに住むかが重要です。localStorageに置かれたJWTはページ上のすべてのスクリプトが読めるため、XSSの欠陥1つで、有効期限まで有効なベアラー資格情報が持ち出されます――サーバー側には失効させる手段がありません。HttpOnly、Secure、SameSiteのCookieに入った不透明トークンはJavaScriptから見えず、XSSの爆発半径を抑えます。その代償はCSRFへの露出――ブラウザはCookieを自動的に添付します――で、SameSiteとCSRFトークンで緩和します。

JWTはCookieに乗ることもできます(多くの場合そうすべきです)。CSRFへの注意点は同じです。AuthorizationヘッダーはCSRFを回避しますが、トークンを再びJavaScriptの射程内に置きます。XSSに対して安全な保存方法は存在しません――あるのは、攻撃者が盗めるものを制限する方法だけです。

サイズ、そしてJWTが実際に明かすもの

典型的なログイン用JWTは、いくつかのクレームで800バイト以上になり、すべてのリクエストで繰り返されます――一方Cookieは1ドメインあたり約4 KBが上限です。不透明なセッショントークンは条件に関わらず16〜32バイトのままです。規模が大きくなると、この違いはヘッダー、プロキシ、ログに現れます。

ペイロードはbase64urlです――エンコードであって暗号化ではありません――なのでトークンを手にした誰もが、PIIや権限を含むすべてのクレームを読めます。暗号化トークン用のJWEも存在しますが、ほとんど使われません。下のデコーダーに実際のトークンを貼り付けてみてください。ヘッダーとペイロードはブラウザ内で展開され、署名は表示されますが検証はされません――検証には秘密鍵が必要で、このツールがそれを要求することはありません。

項目サーバーセッションJWT(RFC 7519)
真実の居場所サーバー側のストア署名済みトークンの中
失効即時――レコードを削除expの時点、または状態の追加による
トークンサイズ16〜32バイトの不透明データ典型的なクレームで~800 B+
検証コストリクエストごとにストア照会署名検証のみ、照会なし
複数サービスの認証共有セッションストアが必須鍵を持つ任意のサービス
有効期限サーバー側のTTL、変更可能expクレーム、発行時に固定
ブラウザでの保存HttpOnly + SameSiteのCookieCookie、またはlocalStorage(XSSリスク)

どちらをどの場面で選ぶか

1つのアプリケーションがログインを担うならセッションを選びましょう。サーバーレンダリングのサイト、管理パネル、即時のログアウトとデバイス管理が1回のRedis照会に見合うあらゆるものです。検証が多くの場所で起こらなければならないなら――マイクロサービス、モバイルアプリ、チーム横断のサービス――JWTを選びましょう。そのすべてのために共有セッションストアを持つほうが重い代償です。

指針はきれいに反転します。JWTを失効可能にするために拒否リストやローテーションサービスを築いた瞬間、セッションストアがこっそり戻ってきていないか自問してください。セッションストアが十数個のサービスのボトルネックになった瞬間、それらの間を短命のJWTにするほうが軽いのではないか自問してください。ハイブリッド――ブラウザには不透明なCookie、サービス間にはJWT――は妥協ではなく、正常な最終状態です。

よくある質問

JWTをどこにもアップロードや送信せずに、ここでデコードできますか?

はい。デコーダーは完全にブラウザ内で動作します。ヘッダーとペイロードは単なるbase64urlなので、リクエストをタブの外に出すことなくローカルで展開されます――サイトのContent-Security-Policyが外向きの呼び出しをブロックします。署名は確認用に表示されますが、検証は決してされません。検証には署名鍵が必要で、このツールがそれを要求することはありません。

JWTは暗号化されていますか?

いいえ。RFC 7519が定義するのは署名済みトークンです。ペイロードはbase64url――手にした誰もが読めます――で、署名は発行者が誰かと改ざんされていないことしか証明しません。暗号化は別のプロファイル(JWE)で、実務のシステムはほとんど使いません。すべてのクレームを公開情報として扱ってください。識別子やロールは構いませんが、秘密やPIIは論外です。

JWTを即座に失効させるにはどうすればいいですか?

トークンだけでは不可能です――これがステートレスの取引です。実際のシステムは状態を足し戻します。盗まれたトークンが早く死ぬようリフレッシュトークンのローテーション付きの短いexp期間、jtiクレームを鍵にした拒否リスト、ログアウト時に繰り上げるトークンバージョンクレーム。3つとも、手順の多いセッションストアです。

トークンの保存には、localStorageよりHttpOnly Cookieのほうが安全ですか?

XSSに対しては、はい。localStorageはページ上のすべてのスクリプトが読めるため、注入1回で有効期限まで有効なベアラートークンが盗めます。HttpOnly CookieはJavaScriptから見えないため、同じ注入では盗めません。Cookieの代償はCSRFで、SameSiteとCSRFトークンで対処します。HttpOnlyはXSSを打ち破りません――戦利品からトークンを外すだけです。

マイクロサービスがセッションよりJWTを好むのはなぜですか?

検証がローカルになるからです。RS256なら各サービスは公開鍵を持ち、共有セッションストアへの呼び出しなしにトークンを検証できます。新しいサービスは統合ではなく鍵とともに起動します。不透明なセッションは、すべてのサービスを1つのストアの可用性次第に置きます。その独立性こそステートレスが買うもので――失効はその請求書です。

JWTはどのくらい大きくなると問題になりますか?

まずCookieに注意してください。ブラウザは1ドメインあたり約4 KBで上限を設けており、クレームの多いJWTはそれに近づき、リクエストを丸ごと壊しかねません。上限未満でも、~800バイトのトークンはすべてのリクエストに乗ります。一方、不透明なセッショントークンは条件に関わらず16〜32バイトのままです。クレームは絞ってください。同一性と有効期限であり、プロフィールデータではありません。

ここで引用した規格はどこで読めますか?

  • RFC 7519 — JSON Webトークン(JWT)