ぱんだツールズぱんだツールズ

セキュリティ

JWTとは — 認証トークンの構造と安全な使い方

約7分

APIリクエストに Authorization: Bearer eyJhbGci... ヘッダーを付けていたら 「Invalid token」エラーが返ってきた——そんな経験はないでしょうか。 JWTは現代のWeb開発で欠かせない認証トークン形式ですが、「デコードできる=暗号化されていない」という 意外な事実や、ローカルストレージへの保存が危険な理由など、知っておかないと痛い目を見る落とし穴が いくつかあります。この記事では、JWTの仕組みを構造から順に解説します。

JWTとは

JWT(JSON Web Token、ジョット)は、JSONデータをコンパクトに表現してデジタル署名を付与するための オープン標準(RFC 7519)です。Web APIの認証・認可、マイクロサービス間の信頼確立、シングルサインオン (SSO)など、主にユーザーのログイン状態を安全にやり取りする場面で広く使われています。

従来のセッションベース認証では、サーバーがメモリやDBでセッション情報を管理していました。 JWTではユーザー情報と有効期限をトークン自体に埋め込んで署名するため、 サーバーがセッション情報を保持する必要がなく、複数サーバーへのスケールアウトが容易です。

JWTの3つの構成要素

JWTは3つのパーツをピリオド(.)で区切った文字列です。

# JWTの構造

ヘッダー.ペイロード.署名

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMyIsImV4cCI6MTc0NTI0MDAwMH0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

各パーツはBase64URLエンコードされています。Base64URLはURLセーフな文字のみを使う変換方式で、 暗号化ではありません。したがってデコードすれば誰でも中身を読むことができます。

ヘッダー(Header)

使用するアルゴリズム(alg)とトークンの種類(typ)を宣言します。

// ヘッダーのデコード例

{

"alg": "HS256",

"typ": "JWT"

}

ペイロード(Payload)

ユーザー情報や有効期限などの「クレーム(claim)」を格納するJSONオブジェクトです。

// ペイロードのデコード例

{

"sub": "user_123",

"name": "田中太郎",

"role": "admin",

"iat": 1745236400,

"exp": 1745240000

}

署名(Signature)

ヘッダーとペイロードをつなげた文字列を、秘密鍵でハッシュ化した値です。 受信者は同じ計算を行い、署名が一致すれば「改ざんされていない」と確認できます。 逆に言えば、署名の検証なしにペイロードを信頼してはいけません。

ペイロードに含まれる標準クレーム

RFC 7519では以下の「登録済みクレーム(Registered Claims)」が標準化されています。

クレーム名正式名説明
issIssuerトークンの発行者"example.com"
subSubjectトークンの主体(ユーザーID等)"user_123"
expExpiration Time有効期限(UNIXタイムスタンプ)1745240000
iatIssued At発行日時(UNIXタイムスタンプ)1745236400
jtiJWT IDトークンの一意識別子(リプレイ攻撃防止)"abc-123-xyz"

これらに加えて、アプリケーション固有の情報(ユーザー名・権限ロールなど)を プライベートクレームとして自由に追加できます。ただし機密情報の埋め込みは避けましょう。

署名の仕組み — HS256とRS256の違い

署名に使うアルゴリズムは主に2種類あります。

アルゴリズム方式署名検証向いている構成
HS256共通鍵(HMAC-SHA256)共有秘密鍵同じ共有秘密鍵単一サービス・シンプルな構成
RS256公開鍵暗号(RSA-SHA256)秘密鍵(発行者のみ)公開鍵(誰でも取得可)マイクロサービス・IDaaS連携

HS256では署名も検証も同じ「共有秘密鍵」を使います。実装がシンプルで高速ですが、 複数サービスに秘密鍵を共有すると、1か所が侵害されただけで全サービスが危険にさらされます。

RS256では秘密鍵でしか署名できないが、公開鍵があれば誰でも検証できます。発行サービス(認証サーバー)は秘密鍵を厳重に管理し、各マイクロサービスには公開鍵だけを配布すれば 検証が可能です。AWSのCognito・Google・Auth0などIDaaSの多くがRS256を採用しています。

デコードと検証の違い

ここが最も重要な概念です。

デコードとは、Base64URLエンコードされたヘッダー・ペイロードを元のJSONに戻す操作です。 これは誰でも・鍵なしで・即座にできます。JWTデコードツールでJWTを貼り付けるだけでペイロードの中身が確認できます。

検証(Verification)とは、署名が正しいかを確認する操作です。 HS256であれば共有秘密鍵、RS256であれば公開鍵が必要です。 検証なしにペイロードを信頼することは、「封筒を開けた字が本物かどうか確かめずに署名する」のと同じです。

重要

JWTはデコードできますが、それだけでは「改ざんされていない」とは証明できません。 サーバー側では必ず署名の検証を行ってからペイロードの内容を使用してください。

セキュリティ上の注意点

ローカルストレージへの保存は危険

JWTをJavaScriptの localStoragesessionStorage に保存すると、 XSS(クロスサイトスクリプティング)攻撃でサードパーティスクリプトが実行された際にトークンが盗まれます。 盗まれたトークンを使って攻撃者がAPIにアクセスし放題になります。

推奨は HttpOnly属性付きのSecure Cookie への保存です。HttpOnly 属性があるCookieはJavaScriptから読み取れないため、 XSS経由での盗取を防げます。あわせてCSRF対策として SameSite=Strictまたは SameSite=Lax の設定も必要です。

有効期限を短く設定する

JWTはサーバー側に状態を持たないため、一度発行したトークンを即時無効化することが難しいです。 ユーザーがログアウトしても、JWTの有効期限が残っていれば技術的には使い続けられます。

対策は2つです。①expを短くする(15〜60分程度)、 ②リフレッシュトークンと組み合わせる(アクセストークンは短命、リフレッシュトークンで再発行)。 重要操作(決済・パスワード変更など)では再認証を要求する仕組みも有効です。

ペイロードに機密情報を含めない

前述の通り、ペイロードは誰でもデコードできます。 パスワード・クレジットカード情報・詳細な個人情報(住所・電話番号など)はペイロードに含めてはいけません。 ユーザーIDや権限ロールなど、サーバーが処理に使う最小限の情報のみを格納するのが原則です。

まとめ

  • JWTはヘッダー・ペイロード・署名の3パーツで構成され、ピリオドで区切られる
  • ペイロードはBase64URLエンコードされているだけで、誰でもデコードして読める
  • デコードと検証は別物。サーバーでは必ず署名を検証してからペイロードを信頼する
  • HS256は共有秘密鍵、RS256は公開鍵方式。複数サービスではRS256が安全
  • JWTはlocalStorageに保存せず、HttpOnly Cookieに保存する
  • 有効期限(exp)は短く設定し、リフレッシュトークンと組み合わせる
  • 機密情報はペイロードに含めない

よくある質問

JWTとセッションの違いは何ですか?

セッションはサーバー側でユーザーの状態を管理し、クライアントはセッションIDだけを持ちます。JWTはユーザー情報(クレーム)をトークン自体に埋め込んで署名するため、サーバー側でセッション情報を保持する必要がありません。スケールアウトが容易でマイクロサービスに向いている反面、一度発行したトークンを即時無効化するには追加の仕組み(ブラックリストなど)が必要です。

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

標準的なJWT(JWS形式)は暗号化されておらず、Base64URLエンコードされているだけです。ヘッダーとペイロードは誰でもデコードして中身を読むことができます。暗号化されたJWT(JWE形式)という仕様も存在しますが、一般的に「JWT」と呼ばれるものはJWSであり、署名による「改ざん検知」が目的であって「秘密保持」は目的ではありません。機密情報はペイロードに含めないようにしましょう。

JWTのペイロードは誰でも読めますか?

はい、読めます。JWTのペイロードはBase64URLエンコードされているだけなので、デコードすれば誰でも内容を確認できます。署名の検証なしに「デコードだけ」することは技術的に可能です。これが重要なポイントで、ペイロードには「ユーザーIDや権限情報などは含めてよい」が「パスワード・クレジットカード番号・個人を特定できる機密情報は含めてはいけない」という原則につながります。

JWTの有効期限が切れたらどうすればいいですか?

expクレームに設定した時刻を過ぎたJWTはサーバーが拒否します。一般的な対処法はリフレッシュトークンの仕組みです。アクセストークン(短命:15分〜1時間)とリフレッシュトークン(長命:数日〜数週間)を組み合わせ、アクセストークンが期限切れになったらリフレッシュトークンで新しいアクセストークンを再発行します。リフレッシュトークンはHttpOnly CookieやサーバーサイドDBで安全に管理します。

HS256とRS256どちらを使えばいいですか?

単一サーバー・マイクロサービスが少ない構成ではHS256(共有秘密鍵・実装がシンプル)で十分です。複数サービスがトークンを検証する場合や、サードパーティにトークン検証だけ許可したい場合はRS256(公開鍵で検証可能)を使います。AWSやAuth0などのIDaaSはRS256を採用しています。秘密鍵を複数サーバーで共有するリスクを避けたい場合もRS256が適しています。

JWTをローカルストレージに保存してはいけないのですか?

XSS(クロスサイトスクリプティング)攻撃でJavaScriptが実行されると、localStorageのJWTが盗まれる危険があります。攻撃者はそのトークンを使ってAPIにアクセスできます。推奨は「HttpOnly属性付きのSecure Cookie」での保存です。HttpOnly CookieはJavaScriptからアクセスできないためXSSで盗まれません。ただしCSRF対策(SameSite属性やCSRFトークン)も併せて必要になります。

JWTをブラウザでデコードするにはどうすればいいですか?

ぱんだツールズのJWTデコードツールをお使いください。JWTの文字列を貼り付けるだけで、ヘッダー・ペイロードをJSON形式で表示します。ファイルはサーバーに送信されず、すべてブラウザ内で処理されるため、機密性の高いトークンでも安全にデコードできます。署名の検証はできませんが、ペイロードの内容確認・有効期限の確認(expクレーム)に使えます。

この記事で紹介したツール