JWT
JSON Web Token
概要(サマリー)
JWT(JSON Web Token、ジェイダブルティー)とは、Webアプリのシステム間で、ユーザーや権限に関する情報を署名付きで受け渡すためのトークン形式である。
たとえば、あなたがテーマパークの年間パスポートを持っているとする。そのパスポートには、あなたの名前や有効期限が書いてあり、かつパーク側が「本物である」と証明するハンコ(署名)が押されている。このパスポートを見せるだけで、各アトラクションのスタッフは毎回本人確認をデータベースに問い合わせることなく、中に入れる。
この「電子ハンコ付きのパスポート」を、Web上でやり取りしやすいテキスト(JSON形式)にしたものがJWTである。ログイン状態の維持や、異なるWebサービス同士を安全に連携させるための認証に広く使われている。
詳細解説
JWTの構造(3つの部分)
JWTはドット(.)で区切られた3つの文字列(Base64URLでエンコードされたもの)で構成されている。
xxxxx.yyyyy.zzzzz
- ヘッダー(Header): トークンの種類や、電子署名に使うアルゴリズム(例:HMAC SHA256やRSA)が記載されている。
- ペイロード(Payload): 実際にやり取りしたい情報(ユーザーID、有効期限、権限など)。この中に含まれる1つのデータ単位を「クレーム(Claim)」と呼ぶ。
- 署名(Signature): ヘッダーとペイロードを組み合わせ、サーバー側の秘密鍵や秘密鍵に対応する鍵を使って生成した署名。これにより、途中でトークンの中身が改ざんされていないかを検証できる。
JWTが使われる基本的な流れ
- ユーザーがブラウザからユーザー名とパスワードを送信してログインする。
- サーバー側で認証し、問題がなければユーザー情報(ユーザーIDなど)を含んだJWTを生成する。
- サーバーはJWTに署名を付与し、ブラウザへ返す。
- ブラウザは受け取ったJWTを保持し、次回以降のリクエスト(APIへのアクセスなど)のヘッダー(例:
Authorization: Bearer <JWT>)に載せて送信する。 - サーバーは送られてきたJWTの署名、有効期限、発行者、対象APIなどを確認し、問題がなければユーザーを認証してサービスを提供する。
JWTのメリットとデメリット
- メリット:
- ステートレスにしやすい: サーバー側でユーザーのログインセッションを細かく保持しなくても、署名の検証でトークンの正しさを確認できる。
- API連携に向いている: サーバーが複数台あるシステム(クラウド環境)や、別ドメインのAPIと連携する際に、認証情報を決まった形式で渡しやすい。
- デメリット:
- 途中で無効化しにくい: 一度発行したJWTは、有効期限が切れるまでサーバー側から強制的に無効化(ログアウト)させることが難しい。そのため、有効期限を短く設定し、「リフレッシュトークン」と組み合わせる設計が一般的である。
- データが暗号化されているわけではない: Base64URLでエンコードされているだけなので、誰でもデコードして中身を読むことができる。そのため、JWTのペイロードの中にパスワードなどの個人情報を入れてはならない。
JWTで必ず確認すべきクレーム
JWTのペイロードには、標準的なクレームを入れられる。
代表的なものには、有効期限を表す exp、発行者を表す iss、トークンの対象を表す aud、ユーザーなどの主体を表す sub がある。
JWTを受け取る側は、署名だけでなく、これらの値も確認する必要がある。たとえば、別のサービス向けに発行されたJWTを自分のAPIで受け入れてしまうと、本来通してはいけないリクエストを許可してしまう危険がある。
保存場所によってリスクが変わる
ブラウザアプリでJWTを使う場合、どこに保存するかも重要である。
LocalStorageに保存するとJavaScriptから扱いやすいが、XSSがあると盗まれやすい。一方、HttpOnly属性付きのCookieに保存するとJavaScriptから読まれにくいが、設計によってはCSRF対策が必要になる。
つまり、JWTを使うだけで安全になるのではなく、保存場所、送信方法、有効期限、リフレッシュトークンの扱いを含めて設計する必要がある。
AIコーディングとの関係
AIとJWT認証の実装
AIを使って「ログイン機能付きのWebアプリ」や「モバイルアプリ用のAPI」を作ろうとすると、AIはJWTを使ったトークン認証を非常によく提案し、コードを出力する。
ただし、AIが出力したコードでは「有効期限(Expiration)の設定が漏れている」「秘密鍵(Secret Key)がハードコードされている」といったセキュリティ上のリスクが含まれることがある。
AIに実装させる際は、環境変数(.env)から秘密鍵を読み込み、有効期限を適切に設定し、iss、aud、exp などの検証も行うよう指示することが重要である。
指示を出す際のポイント
AIにJWTの実装を依頼する際は、以下のように具体的に指示すると安全なコードが生成される。
- 「Node.js(Express)と
jsonwebtokenライブラリを使って、ユーザーIDをsubに含むJWTを生成するコードを書いて。有効期限は15分、issuerとaudienceを設定し、秘密鍵は環境変数から読み込むようにして」 - 「ReactからJWTを含めてAPIを叩く際、
Authorization: Bearerヘッダーにアクセストークンを設定する方法を示して。LocalStorage保存とHttpOnly Cookie保存のリスク差も説明して」
よくある勘違い
JWTは中身を暗号化して隠すためのもの?
JWT(通常の署名付きJWT: JWS)は、データを「隠す(暗号化する)」ものではなく、「改ざんを防ぐ(署名する)」ものである。
JWTの文字列はツールを使えば誰でも簡単に元のJSON形式にデコードできるため、中身は丸見えである。そのため、クレジットカード番号やパスワードなどの機密データをJWTの中に含めてはいけない。もしデータを暗号化したい場合は、JWE(JSON Web Encryption)という別の仕様を使う必要がある。
JWTがあればセッションやCookieは完全に不要になる?
必ずしもそうではない。
JWTはデータを格納する「フォーマット」であり、ブラウザ側でそれをどう保存するか(Cookieに保存するか、LocalStorageに保存するか)は別の問題である。
また、ユーザーを強制的に即時ログアウトさせたい管理機能などがあるシステムでは、依然としてサーバー側でログイン状態を管理する「セッション」方式の方が適していることもある。
JWTの署名キー(秘密鍵)は推測しやすくてよい?
署名に使う秘密鍵は、極めて強力で複雑な文字列にする必要がある。
もし秘密鍵が「password123」のように単純な場合、ハッカーによって秘密鍵を破られ、ユーザー権限を「管理者(admin)」に改ざんした偽のJWTを勝手に作られてしまう。秘密鍵はランダムな長い文字列を生成し、決して外部に漏らしてはならない。
署名を検証せずにデコードするだけでよい?
JWTは、単にデコードするだけでは信頼できない。
攻撃者はペイロードを書き換えたJWTを簡単に作れるため、受け取った側は必ず署名を検証し、許可したアルゴリズム、有効期限、発行者、対象APIを確認する必要がある。特に、ライブラリの設定ミスで none アルゴリズムや想定外のアルゴリズムを受け入れないようにすることが重要である。
まとめ
- JWTは、Webアプリ間でユーザー情報や認証状態を安全にやり取りするための、署名付きJSONデータフォーマットである。
- セッションを完全に置き換えるものではなく、API連携やステートレスな認証設計で使われやすい。
- Base64URLで変換されているだけなので中身は誰でも読める。機密情報は絶対に含めてはならない。
- JWTを受け取る側は、署名だけでなく
exp、iss、audなどのクレームも検証する必要がある。 - AIにJWT認証を実装させる際は、有効期限、秘密鍵管理、保存場所、失効設計まで明示して依頼する。
情報ソース
- JWT.io (Official Website & Debugger)
- RFC 7519: JSON Web Token (JWT) Specification
- RFC 8725: JSON Web Token Best Current Practices
- OWASP Cheat Sheet Series: JSON Web Token Cheat Sheet
より詳しくAIに聞いてみよう
- JWTのペイロード(Payload)によく設定される標準的なクレーム(iss, sub, aud, exp, iatなど)のそれぞれの意味を教えてください。
- ブラウザのLocalStorageにJWTを保存する場合と、HttpOnly属性のCookieに保存する場合のセキュリティ(XSSやCSRF)のメリット・デメリットを比較してください。
- Python(PyJWT)を使って、ログイン成功時にJWTを発行し、別のAPIでそのJWTを検証(デコード)するシンプルなコード例を示してください。
- 有効期限が切れたJWTを自動で更新するための「リフレッシュトークン」の仕組みと、サーバー・クライアント間の通信の流れを詳しく解説してください。
- AIに「JWTを使ったセキュアなSPA向け認証API」の実装コードを書いてもらうためのプロンプトの例を教えてください。