JWTとは|仕組み・セッション認証との違い・実装の注意点
最終更新日:2026/08/06
JWT(JSON Web Token)とは、ユーザー情報や有効期限などをJSONにまとめ、署名付きで改ざんを検知しながら受け渡せるトークン形式です。サーバー側に状態を持たせずに認証情報を運べる反面、即時失効の難しさや保管場所の設計を誤ると事故につながります。「セッションと何が違うのか」「案件で聞かれたときにどこまで踏み込めば十分か」を整理したいWeb/API開発に関わるフリーランスエンジニア向けに、構造・脆弱性対策・実装判断のポイントまでまとめます。
先に結論
JWTは「署名付きで改ざん検知できるJSONトークン」であり、暗号化ではない
セッション認証と違い、サーバーに状態を持たせずに水平スケールしやすい
ただし即時失効ができないため、短寿命アクセストークン+リフレッシュトークンが基本
localStorageにJWTを保存するのはXSSに弱い。セキュリティ要件の高いWebアプリでは、HttpOnly Cookie+BFF構成が有力な選択肢
実装では alg=none禁止/署名検証/有効期限(exp)/HTTPS必須 を最低限おさえる
この記事でわかること
JWTの構造と署名の仕組み(暗号化との違い)
セッション認証との違いと使い分けの判断軸
主要クレームと発行〜検証の流れ
実装で踏みやすい落とし穴(alg=none、鍵管理、保管場所、失効)
フリーランス案件で求められる知識レベルと関連案件の広がり
目次
JWT(JSON Web Token)とは何か
JWTの構造(3パート)
主要クレームとその役割
JWTとセッション認証の違い
認証と認可でのJWT活用(OAuth 2.0との関係)
実装で気をつける5つの落とし穴
フリーランスエンジニアが問われる知識レベル
よくある失敗と対策
JWT実装チェックリスト
まとめ
よくある質問
JWT(JSON Web Token)とは何か
JWTとは、署名により改ざん検知できるJSON形式のトークンです。認証・認可に必要な情報をJSONで表現し、署名で改ざんを検知できるようにしたトークンの形式です。RFC 7519で標準化されており、SPA・モバイルアプリ・API・シングルサインオン(SSO)など、サーバー間・クライアント間で信頼できる情報を渡す用途で使われます。
RFC 7519の位置づけ
JWTの仕様はIETFのRFC 7519で定義されています。関連仕様として、署名を扱うJWS(RFC 7515)、暗号化を扱うJWE(RFC 7516)、公開鍵の配布を扱うJWK/JWKS(RFC 7517)があります。JWTは入れ物の総称で、署名付きならJWS、暗号化付きならJWEとして扱われます。実務で「JWT」と呼ばれるものはJWSで署名されたトークンを指すことが多いです。
JWTは「認証情報の入れ物」であって認証方式ではない
ここが最初に混乱しやすい点です。JWTはトークンの表現形式であり、「JWTを使う=認証方式」というわけではありません。実際には次のような組み合わせで登場します。
ログイン後のアクセストークンをJWT形式で発行する
OAuth 2.0のIDトークン(OpenID Connect)をJWT形式で発行する
マイクロサービス間の内部通信トークンをJWT形式で発行する
この区別が曖昧なまま「JWT認証」と一括りにすると、あとで「OAuthのフローと関係あるのか」で混乱するので、最初に切り分けておくと楽です。
よくあるユースケース
SPA / モバイルアプリ:ステートレスなAPIリクエストの認証情報として
マイクロサービス間:サービス境界をまたぐ信頼できるユーザーコンテキストの伝播
SSO / フェデレーション:OIDCのIDトークンとして
一時URLの発行:ダウンロードリンクなどの短期権限付与
ミニFAQ:JWTは暗号化されているのですか?
いいえ。デフォルトのJWT(JWS)はBase64URLエンコード+署名であり、ペイロードは誰でもデコードできます。暗号化したい場合はJWE(RFC 7516)を使いますが、実装コストが上がるため、実務ではJWSのペイロードに機密情報を入れないほうが現実的です。
JWTの構造(3パート)
JWTは 「ヘッダー.ペイロード.署名」 の3パートをドット(.)で連結した文字列です。各パートはBase64URLエンコードされた状態で渡されます。
ヘッダー(Header)
トークンの種別(typ)と署名アルゴリズム(alg)を表すJSONです。
例:
キー | 値の例 | 意味 |
|---|---|---|
typ | JWT | トークン種別 |
alg | HS256, RS256, ES256 | 署名アルゴリズム |
ペイロード(Payload)
「クレーム」と呼ばれる主張の集合で、ユーザーIDや有効期限などをJSONで格納します。ここは暗号化されていないので、ペイロードにパスワードやクレジットカード番号など、漏れて困る情報は入れません。
署名(Signature)
ヘッダーとペイロードをBase64URLでエンコードし、ドットで連結した文字列に対して、共通鍵または秘密鍵で署名したものです。受け取り側は同じ鍵(HMACの場合は共通鍵、公開鍵署名の場合は公開鍵)で署名を検証し、改ざんされていないかを確認します。
トークン全体のイメージ:
位置 | 内容 | エンコード |
|---|---|---|
1つ目 | ヘッダーJSON | Base64URL |
2つ目 | ペイロードJSON | Base64URL |
3つ目 | 署名バイト列 | Base64URL |
ミニFAQ:Base64URLエンコードとBase64の違いは?
Base64URLは、URL・ファイル名で使えない「+」「/」を「-」「_」に置き換え、末尾のパディング「=」を省略した形式です。JWTがURLパラメータやHTTPヘッダーで安全に運べるようにするための工夫です。
主要クレームとその役割
クレームは大きく3種類あります。
登録済みクレーム(Registered Claims):仕様で予約された標準クレーム
公開クレーム(Public Claims):IANAに登録された、共有可能なクレーム
プライベートクレーム(Private Claims):発行者と受け手で合意した独自クレーム
実務で最低限おさえたい登録済みクレームは以下です。
クレーム | 意味 | よくある使い方 |
|---|---|---|
iss | 発行者(Issuer) | 認証サーバーのURLなど |
sub | 主体(Subject) | ユーザーID |
aud | 対象者(Audience) | このトークンを受け取るAPIの識別子 |
exp | 有効期限(Expiration) | Unix時間。必須級 |
iat | 発行時刻(Issued At) | Unix時間 |
nbf | 有効開始時刻(Not Before) | 未来時刻に発行するトークンなど |
jti | JWT ID | 二重発行検知・失効リスト管理 |
expは仕様上の必須ではないものの、実務ではほぼ必須です。省略するとトークンが半永久的に有効になり、後述する「失効の難しさ」が致命的になります。
独自のプライベートクレーム(role、tenant_id など)を入れる場合は、他のシステムとのぶつかりを避けるために、逆ドメイン形式のキー(例:https://example.com/role)で名前空間を切る流儀もあります。
JWTとセッション認証の違い
「JWTとセッションはどちらが良いか」は案件でよく問われますが、優劣ではなく「状態をどこに持つか」の設計思想が違う、と捉えるのが実務的です。なお表内の「Bearerトークン」は、提示した人をそのまま利用者として扱うトークン方式を指します。
比較表:JWT vs セッション認証
観点 | セッション認証 | JWT(Bearerトークン) |
|---|---|---|
状態の持ち方 | サーバー側のセッションストアに保持 | クライアント側に保持(ステートレス) |
スケーラビリティ | セッションストアの共有が必要 | 検証だけならストア不要で水平スケールしやすい |
即時失効 | セッション削除で即時失効 | 有効期限まで無効化しづらい(失効リストが必要) |
トークンサイズ | 通常小さい(ID文字列) | クレーム量に比例して大きくなる |
XSSリスク | HttpOnly Cookieで軽減しやすい | localStorage保管だと窃取されやすい |
CSRFリスク | Cookieベースでは対策が必要 | Authorizationヘッダーで送るとCSRFは低下 |
実装難易度 | 定番パターンが多く低い | 署名・失効・保管の設計に注意が必要 |
セッション認証の特徴
伝統的なWebアプリで主流の方式です。ログインするとサーバーがセッションを発行し、SessionIDをCookieに入れて返します。ユーザーの状態はサーバー側で管理するため、「ログアウトさせる」「BANする」「別デバイスから追い出す」がやりやすいのが強みです。
JWTの特徴
サーバーは署名検証のみで認証が完結するため、ロードバランサー配下の複数サーバーやマイクロサービス間で状態共有をしなくてよいのがメリットです。反面、有効期限が切れるまで無効化しにくく、緊急時の即時失効には別途仕組み(ブラックリスト・鍵ローテーション)が要ります。
判断フロー:ざっくり選ぶなら
サーバーが1〜数台、モノリスの管理画面や社内システム → セッション認証で十分
SPA・モバイルアプリ・複数APIを跨ぐ → JWTまたはBFF経由のトークンベース認証が向く
マイクロサービスで信頼できるユーザーコンテキストを伝播 → JWT系の署名付きトークンが定番
即時失効・きめ細かい権限変更が最優先 → セッション寄り(またはJWT+短寿命+失効管理)
「新しくてかっこいいからJWT」で選ぶと、失効設計で詰まるケースが多いです。要件から逆算するのが安全です。
ミニFAQ:モノリスな社内システムでもJWTを使うべき?
必ずしも必要ありません。単一サーバー・単一アプリでセッションストアの共有問題がないなら、セッション認証のほうが即時失効も含めて素直に組めます。「マイクロサービスにいずれ分割する予定」「モバイルアプリを追加する」など、ステートレスが効いてくる予定があるときに検討する価値が出てきます。
認証と認可でのJWT活用(OAuth 2.0との関係)
「JWTとOAuthって同じもの?」もよく混同されるポイントです。結論は次のとおり明確に分けられます。
OAuth 2.0:認可を扱うプロトコル(誰がどのリソースにアクセスできるかのフロー)
JWT:トークンの形式(アクセストークンをどう表現するかの一つの選択肢)
OAuth 2.0のアクセストークンは「クライアントとリソースサーバー間で通じればよい不透明な文字列」でよく、JWT形式にする必要は仕様上ありません。ただし、リソースサーバー側で追加のイントロスペクション(トークン検証API呼び出し)なしに検証したい場合、JWT形式が便利なので広く使われています。
OAuthの認可フローそのものの整理は「OAuth 2.0とは|認可の仕組み・4つのグラントとPKCEを解説」を参照してください。
OpenID Connect(OIDC)のIDトークン
OIDCはOAuth 2.0の上に認証の層を乗せた仕様で、その中で「IDトークン」というJWTを規定しています。IDトークンは必ずJWTである必要がある、というのがOAuth 2.0との違いです。
アクセストークン:リソースへのアクセス権を表す。JWTでなくてもよい
IDトークン:ユーザー本人を表す。JWTで表現する
「OIDCで認証、OAuthで認可、その中身の形式としてJWT」という3層構造で捉えると整理しやすいです。
アクセストークンをJWTにする実装例
Node.js/Express系ならライブラリで数十行、NestJSならガード(Guard)機構と組み合わせて実装する、というのが定番パターンです。「発行→検証→失効」の3工程それぞれで、後述の落とし穴を意識しておくと事故が減ります。
実装で気をつける5つの落とし穴
JWTの事故は、仕様理解の不足というより「デフォルトの緩さを潰していない」ケースがほとんどです。5つに絞って整理します。
① alg=none 攻撃
JWT仕様上は署名アルゴリズムに「none」(署名なし)を指定できます。現代の主要ライブラリでは通常このnoneは拒否されますが、古いライブラリや検証設定が緩い実装では受け入れてしまうケースが残っています。攻撃者がヘッダーのalgをnoneに書き換え、署名部を削除したトークンを送りつけ、検証をすり抜けて任意のユーザーになりすます、という古典的な攻撃です。
対策:
検証側で受け入れるalgを明示的にホワイトリスト化する
ライブラリ側で「algorithms: ['HS256']」のように受け入れアルゴリズムを明示指定する
ヘッダーのalgを鵜呑みにして検証関数を選ばない
現代の主要ライブラリはデフォルトでnoneを拒否する実装が多いものの、古いライブラリやカスタム実装ではまだ生きているリスクなので、レビュー時に必ず確認します。詳細はAuth0のJWT BCP解説にまとまっています。
② 署名アルゴリズムの取り違え(HS/RS混同攻撃)
RS256(公開鍵署名)で発行しているシステムに対して、攻撃者がヘッダーをHS256に書き換え、公開鍵を秘密鍵として使ってHMAC署名したトークンを送るパターンです。検証側が「アルゴリズムはヘッダーに従う」実装だと通ってしまいます。
対策:
検証時に期待するアルゴリズムを固定する(HS256とRS256を同時に受け入れない)
鍵管理を対称鍵と非対称鍵で明確に分ける
③ 鍵の管理
HS256(共通鍵)の場合、その鍵が漏れた瞬間に任意のトークンを偽造できます。GitHubのリポジトリに.envごと上がってしまう事故は今もあります。
対策:
HS256は共通鍵の共有範囲を最小に。マイクロサービス間で使うならRS256/ES256(公開鍵署名)を検討
鍵ローテーションを想定して、ヘッダーのkid(Key ID)でどの鍵で検証するかを識別できる設計に
鍵はシークレットマネージャ(AWS Secrets Manager等)で管理し、コードにハードコードしない
④ 保管場所とXSS/CSRFのトレードオフ
「JWTはlocalStorageに入れる」がSPAで長く定番でしたが、XSSに1発で全部持っていかれるのが弱点でした。近年はブラウザ側にトークンを露出させない設計が主流になりつつあります。
保管場所ごとの特徴:
保管場所 | XSS耐性 | CSRF耐性 | 特徴 |
|---|---|---|---|
localStorage | 低(JSから読める) | 高(自動送信されない) | 手軽だが窃取リスク大 |
sessionStorage | 低(JSから読める) | 高 | タブ閉じで消える。用途限定的 |
HttpOnly Cookie | 高(JSから読めない) | 低(自動送信される) | CSRF対策が別途必要 |
BFF経由でCookie | 高 | SameSite等で軽減可 | フロントにトークンが出ない |
近年は、セキュリティを重視する構成でBFF(Backend for Frontend)でHttpOnly Cookieを扱い、フロント側はトークンを一切触らない設計がよく採用されます。Next.jsなどのフレームワークがサーバーサイドを持てるようになったこと、サードパーティCookie規制でBFFに寄せる合理性が増したことが背景にあります。
フロントエンドのセキュリティ全般はセキュリティ関連職種の知識と重なります。関連キャリアは「セキュリティエンジニアとは」でも整理しています。
⑤ 有効期限(exp)とリフレッシュトークンの運用
JWTは発行後に無効化しづらいため、有効期限を短くして「漏れても被害が広がらないうちに切れる」設計にするのが定石です。
一般的な目安として:
アクセストークン(JWT):5〜30分程度の短寿命(要件により変動)
リフレッシュトークン:数日〜数週間の長寿命。HttpOnly Cookieに格納
リフレッシュ時に古いリフレッシュトークンを無効化する(Rotation)
即時失効が必要になったら、jtiベースの失効リストや、ユーザー単位のtoken_versionカラムをDBに持ち、検証時に照合するのが実務的です。「ステートレスなはずのJWTに、失効のために状態を戻す」という一見矛盾した設計になるのは、ここが理由です。
ミニFAQ:リフレッシュトークンをJWTにする必要はある?
必ずしも必要ありません。リフレッシュトークンはサーバー側でDBに保存して失効管理する運用が多いので、ランダムな不透明文字列で十分なケースが多いです。JWTの署名検証の恩恵より、確実な失効のほうを優先することが多い、という判断です。
フリーランスエンジニアが問われる知識レベル
JWTは技術面接や案件の設計レビューで頻出です。「知っている」と「実装で判断できる」の間に差が出やすいトピックなので、案件で強みにしやすい領域でもあります。
SPA / BFF案件での設計判断
フロントエンドフレームワーク(React・Next.js・Vue.js)とAPIの構成を設計する案件で、「トークンをどこに置くか」の判断を求められます。BFF構成のメリット・デメリットを、案件の規模・チーム体制と紐付けて話せると印象が変わります。ランタイム選定では「Node.jsとは」「Bunとは」あたりの選択肢が絡みます。
マイクロサービス間のトークン伝播
マイクロサービス案件では、サービス境界をまたぐユーザーコンテキストの持ち回りが必ず論点になります。JWTで公開鍵署名(RS256)を採用し、各サービスは公開鍵で検証、認証サーバーだけが秘密鍵を持つ、というパターンが定番です。
認証基盤(Auth0 / Keycloak / Cognito)活用案件
自前でJWTを発行するより、認証基盤(IDaaS)を採用し、アプリ側ではJWT検証を中心に実装する案件もあります。この領域の案件動向は「認証基盤・IDaaSエンジニアのフリーランス案件動向」で整理しています。JWTを実装レベルで理解していると、IDaaSのカスタマイズ余地・制約を評価しやすくなります。
APIバックエンド案件
REST APIを提供するバックエンド案件では、認証層としてJWTが選ばれることが多いです。API設計の原則は「REST APIとは|設計原則・HTTPメソッド・GraphQL/gRPCとの違い」を参照してください。ランタイム選定では「Node.jsとは」「Express.jsとは」あたりが実装パターンの参考になります。
よくある失敗と対策
現場で実際に見かける事故パターンをまとめます。
ペイロードに機密情報を入れる
JWTは暗号化されていないため、パスワード・API秘密鍵・個人情報などをペイロードに入れると、デコードするだけで丸見えです。ペイロードには「サーバーがユーザーを識別するのに必要な最小限」だけ入れます。
有効期限が長すぎる
「毎回ログインさせるとUXが悪い」という理由で、アクセストークンの有効期限を24時間以上に設定するケースがあります。トークンが漏れたときの被害範囲を考えると、リフレッシュトークンでの再発行に頼り、アクセストークンは短寿命にするのが安全です。
alg混同・alg=noneを許すライブラリ設定
デフォルト設定でalgorithmsを明示していないライブラリを使い、noneや意図しないアルゴリズムを受け入れてしまう設定漏れです。検証コードで必ず「algorithms: ['RS256']」のようにホワイトリスト指定します。
サーバー時刻のズレで exp / nbf が不安定になる
複数サーバー構成で時刻同期がずれていると、あるサーバーでは有効、別のサーバーでは期限切れの判定になります。NTP同期はもちろん、leeway(許容秒数)を数秒〜数十秒設定するライブラリオプションを活用します。
リフレッシュトークンをlocalStorageに置く
「アクセストークンは短寿命だから許容、リフレッシュトークンだけHttpOnly Cookie」というアドバイスに反して、リフレッシュトークンもlocalStorageに置いてしまう構成をたまに見ます。XSSで両方持っていかれると意味がないので、ブラウザベースのWebアプリではリフレッシュトークンをHttpOnly Cookieに置くのが基本です(モバイルアプリ等は別のセキュアストレージを使います)。
JWT実装チェックリスト
案件のコードレビューや設計時に、この10項目を上から潰していくと大きな抜け漏れを防ぎやすいです。
# | 項目 | 確認ポイント |
|---|---|---|
1 | 署名アルゴリズムのホワイトリスト化 | 検証時にalgorithmsを明示。noneは拒否 |
2 | 鍵の管理 | ソースにハードコードなし。シークレットマネージャで管理 |
3 | RS256/HS256の使い分け | サービス間ではRS256/ES256が有力。単一サービスではHS256も選択肢 |
4 | 鍵ローテーションの設計 | kidで識別可能。移行期の複数鍵受け入れ |
5 | exp(有効期限)の設定 | アクセストークンは短寿命(5〜30分目安) |
6 | iss / aud の検証 | 受け入れる発行者・対象者を検証コードで固定 |
7 | leeway(時刻許容) | 数秒〜数十秒の許容を設定 |
8 | リフレッシュトークンの運用 | HttpOnly Cookie格納・Rotation・DB管理 |
9 | 失効の仕組み | 緊急失効の手段(jti・token_version)を設計 |
10 | 保管場所と伝送 | HTTPS必須。BFF検討。localStorage回避 |
まとめ
JWTは署名付きで改ざん検知できるJSONトークンであり、ステートレスな認証設計を可能にする一方で、即時失効や保管場所の設計を誤ると事故につながります。要点を整理します。
JWTはトークンの形式であって、それ自体が認証方式ではない
暗号化されていないため、ペイロードに機密情報を入れない
セッション認証との使い分けは「状態をどこに持つか」の設計判断で決める
実装ではalg=none拒否・アルゴリズム固定・exp設定・HTTPS・保管場所の5点を最低限おさえる
リフレッシュトークンはHttpOnly CookieでRotation運用が現代的な定番
OAuth 2.0・OIDC・IDaaS活用案件で頻出のため、設計レベルで話せると案件強度が上がる
次のステップとして、OAuthの認可フローや認証基盤案件の実情を掘り下げたい場合は、以下の記事を参照してください。
参照リンク
よくある質問
JWTは暗号化と何が違うのですか?
JWT(JWS)は署名による改ざん検知であり、暗号化ではありません。ペイロードはBase64URLでエンコードされているだけで、デコードすれば内容が読めます。暗号化したい場合はJWE(RFC 7516)を使いますが、実装コストが上がるため実務ではペイロードに機密情報を入れない運用のほうが現実的です。
HttpOnly Cookieに保管すればXSSは完全に防げますか?
XSSからの直接窃取は防げますが、サーバーへリクエストする権限そのものは攻撃者が悪用できます(Cookieが自動送信されるため)。XSS対策そのものを怠ってよい理由にはなりません。CSP(Content Security Policy)・入力サニタイズ・出力エスケープの徹底が前提です。
JWTのペイロードにパスワードを入れてもいいですか?
入れてはいけません。JWT(JWS)のペイロードは暗号化されておらず、トークンを持っている人なら誰でもデコードして読めます。パスワード・カード番号・トークンなど、漏れて困る情報は入れないでください。
セッションよりJWTのほうが安全ですか?
一概には言えません。設計次第でどちらも安全にも危険にもなります。単一サーバーの管理画面なら、即時失効しやすいセッション認証のほうが素直な選択です。マイクロサービスや複数クライアント(Web・モバイル)を捌く場合はJWTの利点が効いてきます。
アクセストークンの有効期限は何分に設定すべき?
要件次第ですが、5〜30分が目安として多いです。API呼び出しの頻度、UX(毎回のログイン頻度)、漏洩時の被害の大きさをバランスさせます。金融系など機密度の高い領域ほど短く設定します。
リフレッシュトークンとアクセストークンの寿命の目安は?
一般的なパターンとして、アクセストークン5〜30分・リフレッシュトークン数日〜数週間の組み合わせがよく使われます。長寿命なリフレッシュトークンにはRotation(再発行時に旧トークンを無効化)を組み合わせるとより安全です。
認証基盤(Auth0など)を使う場合でもJWTを理解する必要はありますか?
はい。IDaaSを採用しても、発行されたトークンを検証するのはアプリ側です。iss・aud・expの検証、公開鍵の取得(JWKS)、失効の考え方などは自前で実装するのと同じ知識が必要になります。IDaaS活用案件でも「JWT検証コードのレビュー」は求められがちです。
マイクロサービスでJWTを使うときの注意点は?
公開鍵署名(RS256/ES256)を使い、認証サーバーだけが秘密鍵を持ち、各サービスは公開鍵で検証する構成が定番です。加えて、audで「このサービス向けのトークンか」を検証し、鍵ローテーションを想定してkidを運用します。全サービスで同じ共通鍵(HS256)を共有する構成は、鍵漏洩リスクが広がるので避けます。
OAuth 2.0とJWTは同じものですか?
違います。OAuth 2.0は認可フローのプロトコル、JWTはトークンの形式です。OAuthのアクセストークンをJWT形式で発行することは多いですが、必須ではありません。OIDCのIDトークンだけはJWTで表現する仕様です。
「JWT is Bad」と言われるのはなぜ?
主な批判は「即時失効の難しさ」「ペイロードにデータを入れすぎる誤用」「保管場所の設計ミス」に集約されます。JWT自体が悪いのではなく、セッション認証で十分なところに無理に使うと事故が起きやすい、という指摘です。要件から逆算して選べば、多くの場合は問題になりません。
JWTの学習で最初におさえるべき仕様は?
RFC 7519(JWT本体)、RFC 7515(JWS)、RFC 7517(JWK)、そしてJWT BCP(Best Current Practice)です。RFC 7517は公開鍵を配布するJWK/JWKSの仕様で、認証基盤やIDaaSからの鍵取得で必要になります。まずはjwt.ioでトークンをデコード・生成して手を動かすと、ヘッダー・ペイロード・署名の3層構造の理解が早く進みます。

