セキュリティ

JWTとは?構造・署名検証の仕組みとセッション・OAuth/OIDCとの違いを実装視点で解説

JWTとは?構造・署名検証の仕組みとセッション・OAuth/OIDCとの違いを実装視点で解説

JWT(JSON Web Token)は、JSON形式の情報を署名付きでコンパクトに運ぶトークンの仕様で、RFC 7519として2015年5月に標準化されています。ドットで区切られた3つのパーツにヘッダー・ペイロード・署名を収め、受け取った側が署名を検証するだけで改ざんの有無を判定できるのが特徴です。この記事では、JWTの構造とBase64URL、HS256/RS256の署名検証、PyJWTとNode.jsで発行から検証まで動かす手順、JWKSによる鍵の入れ替え、alg:none対策と保存場所・失効の設計、サーバセッションやOAuth 2.0・OIDCとの違いまでを、実装者が設計判断できる粒度で整理します。API認証や会員基盤にJWTを組み込む開発者が対象です。

まとめ:JWTは署名付きで検証できるトークンの器と実装の要点

JWTは、発行者が秘密鍵または共有鍵で署名したJSONを、受信側が対応する鍵で検証してデータの正当性を確かめるトークンです。各パーツはBase64URLで変換されているだけで暗号化ではないため、ペイロードは誰でも読めます。パスワードや個人情報をそのまま載せない前提で設計します。

実装の勘所は3つに集約できます。1つ目は署名検証で、期待するアルゴリズムをサーバ側で固定し、トークンのヘッダーが名乗るalgを鵜呑みにしないこと。2つ目は失効の弱さへの備えで、JWTはサーバに状態を持たないぶん個別の無効化が難しいため、有効期限を短く切りリフレッシュトークンと組み合わせます。3つ目は保存場所の判断で、localStorageはXSS、Cookieはクロスサイトの偽装リクエストという別々のリスクを抱えるため、httpOnly・SameSite・短命化で守り方を決めるのが定石です。JWT自体はトークンの器であり、OAuth 2.0やOIDCといったプロトコルの中で運ばれる部品だと捉えると、役割の切り分けが見通せます。

実装上の落とし穴はRFC 8725(JWT Best Current Practices、2020年2月)が一覧化しており、レビューのチェックリストに使えます。

JWTの構造:ヘッダー・ペイロード・署名の3パーツとBase64URL

JWTを理解する近道は、1本の文字列がどう組み立てられているかを分解して見ることです。xxxxx.yyyyy.zzzzzのようにドットで3分割された各部が、それぞれ別の役割を持ちます。

ドットで区切られた3パーツの構成とBase64URL変換の仕組み

JWTは、ヘッダー・ペイロード・署名の3つをドットでつないだ文字列です。先頭2つはそれぞれのJSONをBase64URLで変換したもので、末尾の署名はその2つを連結して鍵で計算した値になります。Base64URLはURLやHTTPヘッダーで安全に扱える文字だけを使う変換方式で、記号の扱いが通常のBASE64とは別物です。この署名の手順はRFC 7515(JSON Web Signature)が定めています。

ここで押さえるべきは、Base64URLが暗号化ではなく単なる変換だという点です。ヘッダーとペイロードはデコードすれば中身が読めるため、機密情報を平文で入れると漏えいします。中身を隠したい場合は、署名のみのJWS形式ではなく暗号化を伴うJWE形式を選ぶか、機密データ自体をトークンに載せない設計にします。一般に「JWT」と呼ばれるのは署名付きで中身は読めるJWS形式を指すことがほとんどです。改ざん検知であって秘匿ではない点はハッシュ化とは?暗号化との違いとパスワード保管・改ざん検知の仕組みと同じ筋です。

ヘッダーとペイロードの構造と登録済みクレームの設計で押さえる点

ヘッダーには、トークン種別を示すtypと署名アルゴリズムを示すalgが入ります。ペイロードには「クレーム」と呼ばれる情報の断片を並べ、RFC 7519があらかじめ意味を決めた登録済みクレームを使うのが基本です。独自の項目を足すこともできますが、他規格との衝突を避けるため命名には配慮します。予約済みの名前はIANAのJSON Web Token Claims Registryで確認できます。

実装で扱う頻度が高い登録済みクレームは次のとおりです。値を検証に使う前提で設計します。

クレーム 意味 用途
iss 発行者 発行元の確認
sub 主体 ユーザー識別
aud 宛先 受け手の限定
exp 有効期限 失効の判定
nbf 有効開始 利用開始の制御
jti 一意ID 再送の検知

ペイロードは軽く保つのが原則です。トークンはリクエストごとに送られるため、権限判定に不要な属性まで詰め込むと通信量が膨らみます。認可に必要な最小限の識別子と権限だけを載せ、詳細はサーバ側で引く設計にすると取り回しが軽くなります。運ぶ側の作法はBearer Token(ベアラートークン)とは?Authorizationヘッダーの書き方・JWTとの違いで扱いました。

コマンドラインでJWTをデコードし中身を自分の目で確かめる手順

本番トークンを外部のデコードサイトへ貼るのは避けたいので、標準ライブラリだけで完結するスクリプトを置きます。

# decode_jwt.py : JWTのヘッダーとペイロードを標準ライブラリだけでデコードする
# 使い方: python3 decode_jwt.py "eyJhbGciOi..."
import base64
import json
import sys

token = sys.argv[1]
for name, part in zip(("header", "payload"), token.split(".")[:2]):
    pad = "=" * (-len(part) % 4)          # Base64URLはパディングを省くので足し直す
    data = base64.urlsafe_b64decode(part + pad)
    print(name, json.dumps(json.loads(data), ensure_ascii=False, indent=2))

鍵を1つも渡していないのにalgやexpが読める事実が、ペイロードは秘匿されないという話の実物です。issやaudのずれを疑う障害調査でも効きます。

JWTの署名検証フローとHS256・RS256による鍵方式の使い分け

JWTの安全性は、発行と検証の両側が同じ鍵体系を正しく扱えるかにかかっています。ここを緩めると、偽造トークンを受け入れる致命的な穴です。まず処理の流れを押さえ、次に鍵方式の選択に進みます。

発行から検証までの署名照合とクレーム確認を両輪でそろえる流れ

サーバはログイン認証が成功すると、ユーザーIDや権限をペイロードに詰め、鍵で署名したJWTをクライアントへ返します。クライアントは以降のリクエストで、多くはAuthorizationヘッダーにBearerトークンとしてこれを添える形です。サーバは受け取ったトークンを検証してからリクエストを処理する流れになります。検証の順序は次のとおりです。

  1. トークンをドットで3分割する
  2. ヘッダーとペイロードから署名を再計算する
  3. 付与された署名と一致するか照合する
  4. expやiss、audなどのクレームを検証する

署名照合と有効期限の確認は、どちらか一方でも欠けると防御になりません。署名が通ってもexpを見なければ期限切れトークンを受け入れてしまい、expだけ見ても署名を検証しなければ偽造を素通りさせます。両輪でそろえて初めて検証が成立します。

HS256(共通鍵)とRS256・ES256(公開鍵)の判断基準

署名方式は大きく2系統に分かれます。HS256はHMACによる共通鍵方式で、発行と検証が同じ秘密鍵を共有します。RS256やES256は公開鍵暗号方式で、発行側だけが持つ秘密鍵で署名し、検証側は公開鍵で確かめる仕組みです。識別子と演算の対応はRFC 7518(JSON Web Algorithms)が定義しています。

方式 鍵 向く構成
HS256 共通鍵 単一サービス内
RS256 公開鍵ペア 複数サービス連携
ES256 楕円曲線鍵 鍵を小さく保つ構成

選択の分岐は、鍵をどこまで配るかで決まります。発行と検証が同じサービス内で完結するならHS256が単純で扱いやすい選択です。逆に、認証サーバが発行したトークンを別のマイクロサービスやフロントで検証する構成では、秘密鍵を配り歩かずに公開鍵だけを配布できるRS256やES256が向きます。検証側に共通鍵を渡すと、その鍵で誰でもトークンを偽造できてしまうため、複数サービスをまたぐならHMAC系は避けるのが堅実です。鍵方式そのものは公開鍵暗号方式と共通鍵暗号方式の違いとは?仕組み・アルゴリズム・使い分けで確かめられます。

JWTを実装する手順:トークンの発行・検証・鍵の入れ替えを動かす

ここからは説明を実行できるコードに落とします。HS256、RS256、JWKSの3段階で、版番号は2026年9月時点の実測値です。

PyJWTでトークンを発行し署名と期限を検証するまでの最小手順

PythonではPyJWTが定番で、2026年9月時点の公開版は2.13.0系です。2系はデコード時のalgorithms指定が必須になっています。

# pip install PyJWT==2.13.0
import datetime
import jwt

SECRET = "replace-with-a-32-byte-random-secret"
now = datetime.datetime.now(datetime.timezone.utc)

token = jwt.encode(
    {
        "iss": "https://auth.example.com",
        "sub": "user-1234",
        "aud": "api.example.com",
        "iat": now,
        "exp": now + datetime.timedelta(minutes=10),   # 短命にする
    },
    SECRET,
    algorithm="HS256",
)

claims = jwt.decode(
    token,
    SECRET,
    algorithms=["HS256"],                              # algの言い値を使わない
    audience="api.example.com",
    issuer="https://auth.example.com",
    options={"require": ["exp", "iss", "aud"]},        # 欠けていたら弾く
)
print(claims)

requireはクレームが無いトークンをMissingRequiredClaimErrorで弾く指定で、PyJWTの公式ドキュメントに用例があります。時刻ずれにはleewayを足せます。

Node.jsのjsonwebtokenでRS256の鍵ペアを使い署名する手順

複数サービスで検証するなら公開鍵方式へ切り替えます。まずOpenSSLで鍵ペアを作ります。

# RSA 2048bitの鍵ペアを作る
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private.pem
openssl rsa -in private.pem -pubout -out public.pem

Node.js側はjsonwebtokenを使います。2026年9月時点の公開版は9.0.3系です。

// npm install [email protected]
const fs = require("fs");
const jwt = require("jsonwebtoken");

const privateKey = fs.readFileSync("private.pem");
const publicKey = fs.readFileSync("public.pem");

const token = jwt.sign(
  { sub: "user-1234", scope: "read:orders" },
  privateKey,
  {
    algorithm: "RS256",
    expiresIn: "10m",
    issuer: "https://auth.example.com",
    audience: "api.example.com",
  }
);

// 検証側は公開鍵しか持たない。algorithms を固定して alg の言い値を無視する
const claims = jwt.verify(token, publicKey, {
  algorithms: ["RS256"],
  issuer: "https://auth.example.com",
  audience: "api.example.com",
});
console.log(claims);

ここでalgorithmsを省くと混同攻撃の入口になります。private.pemの扱いはAPIキー管理とは?発行・保管・失効とローテーションの実装が当てはまります。

JWKSから公開鍵を取得して鍵の入れ替えに追随させる組み立て方

公開鍵をファイルで配ると、入れ替えのたびに全サービスの再デプロイが要ります。これを避けるのが、公開鍵をJSONで配信するJWKSです。鍵の形式はRFC 7517(JSON Web Key)が定めており、検証側はヘッダーのkidで鍵を引くため、新旧2本を並べれば無停止で入れ替えられます。

# pip install "PyJWT[crypto]"
import jwt
from jwt import PyJWKClient

JWKS_URL = "https://auth.example.com/.well-known/jwks.json"
client = PyJWKClient(JWKS_URL)

signing_key = client.get_signing_key_from_jwt(token)   # kidで該当鍵を引く
claims = jwt.decode(
    token,
    signing_key.key,
    algorithms=["RS256"],
    audience="api.example.com",
)
print(claims)

Auth0やOkta、Amazon CognitoはJWKSエンドポイントを公開しており、自前で鍵配布を作らずに済みます。Cognitoで検証まで通す作業はCognitoで認証基盤を実装する手順:ユーザープール構築からJWT検証まで、外部IdPに寄せるかの判断はIDaaSとは?SSO・IdPとの違いとSAML・OIDC・SCIM連携にまとめました。

JWTのセキュリティ実装:alg:none・保存場所・失効の設計

JWTの事故の多くは、仕様の理解不足に起因する実装の抜けから起きます。RFC 8725(JWT BCP、2020年2月)にも対策がまとめられており、ここでは実務で外せない論点を具体的に示します。自社サービスへ認証を組み込む設計段階で判断材料にしてください。

alg:noneとアルゴリズム混同攻撃を防ぐ検証ロジックの固め方

もっとも古典的な穴がalgの扱いです。JWTの仕様には署名なしを意味するnoneがあり、検証側がヘッダーのalgをそのまま信じると、攻撃者がalgをnoneに書き換えた無署名トークンを受け入れてしまいます。さらに、RS256を期待するサーバにHS256のトークンを送り、公開鍵を共通鍵として使わせて署名を通す混同攻撃も知られています。

対策は共通していて、検証側で許可するアルゴリズムをホワイトリストで固定し、トークンが名乗るalgを判断材料にしないことです。ライブラリの検証関数には期待するアルゴリズムを明示的に渡し、noneや想定外の方式は問答無用で拒否します。RFC 8725の3.1節も、algヘッダーを信頼せずアプリケーション側が期待する値で検証せよという立場です。検証ロジックを自作せず、実績あるJWTライブラリの現行版に任せる判断が現実的な守りになります。

RFC 8725はalg以外の穴も挙げており、設定へ翻訳すると次の対応です。OAuth 2.0側の注意点はRFC 9700(Best Current Practice for OAuth 2.0 Security、2025年1月)にまとまっています。

落とし穴 コードでの対応
algの言い値を信じる algorithmsで固定する
issを検証しない issuerを明示指定
audを検証しない audienceを明示指定
expが無いまま通す requireでexpを必須化
鍵の使い回し 用途ごとに鍵を分ける

前掲のコードでaudienceとissuerを渡しているのは、この右列の実装です。audを見ないと、A向けのトークンがBのAPIでも通ります。

トークンの保存場所とXSS・CSRFのリスクのトレードオフ判断

発行したJWTをクライアントのどこに置くかも設計判断です。JavaScriptから読めるlocalStorageに保存すると実装は楽ですが、XSSでスクリプトが混入した瞬間にトークンを盗まれます。一方、httpOnly属性を付けたCookieに入れればJavaScriptからは読めなくなるものの、今度はブラウザが自動送信する性質を突かれるクロスサイトの偽装リクエストへの備えが要ります。保存先ごとの寿命の違いはCookie・localStorage・IndexedDBの違いと使い分けで比較しました。

現実的な落としどころは、httpOnly・Secure・SameSite属性を付けたCookieに置き、状態変更を伴う操作にはCSRFトークンを併用する組み合わせです。どちらの保存先を選んでも、有効期限を短く切っておけば盗まれた際の被害窓口を狭められます。「XSSとCSRFのどちらを主リスクと見るか」を先に決め、それに応じて保存先と付随対策をそろえるのが設計の順序です。違いはXSSとCSRFの違いとは?仕組み・被害・対策を実装レベルで比較、併用するトークンはCSRFトークンとは?仕組み・Double Submit CookieとSPAでの実装で扱っています。

ステートレスゆえの失効の弱さとリフレッシュトークンによる補完

JWTの利点であるステートレス性は、失効の面では弱点に転じます。サーバがトークンの一覧を持たないため、発行済みのJWTを個別に無効化するのが難しく、期限が切れるまで有効なままになります。ログアウトやアカウント停止を即座に反映したい要件とは相性が良くありません。

この弱点を埋める定番が、短命なアクセストークンと長命なリフレッシュトークンの二段構えです。アクセス用のJWTは数分から十数分で失効させ、切れたらリフレッシュトークンで再発行します。リフレッシュトークン側はサーバで管理して失効を効かせ、必要な場面で無効化できるようにするのが役割です。寿命とローテーションの具体はリフレッシュトークンとは?更新フローと寿命・ローテーション設計にまとめました。即時失効が厳格に要る領域では、短命JWTにサーバ側のブロックリスト照合を足す設計も選択肢になります。

JWTとセッション・OAuth 2.0・OIDCの関係と使い分け

JWTを検討すると必ず、従来のサーバセッションとの比較や、OAuth 2.0・OIDCとの関係で迷いが生じます。3者は層が異なるため、境界を取り違えると設計が絡まりがちです。ここを整理して選定の判断につなげます。

サーバセッションとJWTのステートフルとステートレスの比較観点

従来のセッション方式は、サーバがセッションIDと本人情報の対応をサーバ側で保持し、クライアントにはIDだけを渡します。状態をサーバが握るため失効は容易な半面、サーバを増やす際にはセッション情報の共有が必要です。JWTは逆で、必要な情報をトークン自体に持たせて状態を持たないため、水平スケールしやすいかわりに個別失効が難しくなります。

観点 サーバセッション JWT
状態 サーバが保持 トークンが保持
失効 即時に可能 個別は困難
スケール 共有が必要 横展開が容易

この対比から、単一サーバで完結する一般的なWebアプリのログイン維持なら、素直なサーバセッションのほうが失効の扱いで有利な場面が多くあります。JWTが生きるのは、サービスをまたぐAPI認証や、状態共有を避けたい分散構成です。サーバセッション側の設計指針はセッション管理の仕組みとセッションID・保存先の選定で整理しています。

JWTはトークンの器・OIDCのIDトークンはJWTという関係

JWTとOAuth 2.0・OIDCは同じ層の競合ではありません。JWTは「情報を署名付きで運ぶ器」の仕様で、OAuth 2.0(RFC 6749)はアクセス権限を委譲する認可の枠組み、OIDCはそのOAuth 2.0の上に認証層を足したプロトコルです。OIDCが発行するIDトークンは、実体がまさにJWTです。つまりJWTはプロトコルの中で使われる部品にあたります。

それぞれの仕組みは個別記事で扱っています。API権限の委譲フローはOAuth 2.0とは?仕組み・認可フローと認証・認可の違いをわかりやすく解説を、OAuth 2.0の上でIDトークン(JWT)をどう発行するかはOIDC(OpenID Connect)とは?仕組み・OAuthとの違いをわかりやすく解説を参照してください。JSONではなくXMLで認証情報を運ぶ企業SSO向けの規格についてはSAMLとは?認証フロー・IdP/SPの仕組みとOAuth・OIDCとの使い分けを実装視点で解説で扱っており、JWTを使うOIDCとの対比で選定の見取り図になります。これらはいずれも提示するたびに同じ主体だと分かるベアラトークンです。提示のたびに属性の一部だけを見せ、提示同士を結び付けられないようにしたい要件では、Coconut認証(ココナッツ認証)の匿名クレデンシャルのような別系統の方式が必要になります。

JWTを採用すべき場面と見送るべき場面の条件付きでの切り分け

採用判断は条件で言い切れます。フロントとAPIが分離したSPAやモバイルアプリ、複数サービスをまたぐAPI認証、外部のOIDCプロバイダと連携する構成では、ステートレスで自己完結するJWTが素直な選択です。短命なアクセストークンとして使う限り、失効の弱点も運用でカバーできます。

逆に、単一サーバで動く従来型のWebアプリで、ログアウトやアカウント停止を即座に効かせたい要件が中心なら、JWTを無理に使わずサーバセッションを選んだほうが設計は素直です。「状態をサーバに持ちたくないか、即時失効を重く見るか」を最初の分岐に置くと、遠回りを避けられます。JWTベースの認証や会員基盤の設計・実装は、認証基盤・ID管理システム開発としてトークン運用や失効設計ごと受託しています。要件整理の段階からご相談ください。

よくある質問

JWTの導入検討でよく挙がる疑問を、実装者の視点で簡潔にまとめます。

JWTのペイロードは暗号化されていますか?

されていません。ヘッダーとペイロードはBase64URLで変換されているだけで、デコードすれば誰でも中身を読めます。JWTが守るのは改ざんの検知であって秘匿ではないため、パスワードや個人情報をペイロードに平文で載せてはいけません。中身を隠す必要があるときは、暗号化を伴うJWE形式を使うか、機密データ自体をトークンに含めない設計にします。

JWTとセッションはどちらを使うべきですか?

要件で分かれます。単一サーバでの一般的なログイン維持や、ログアウトを即座に反映したい用途はサーバセッションが素直です。複数サービスをまたぐAPI認証や、状態共有を避けたい分散構成ではJWTが向きます。JWTを使う場合も、有効期限を短く切りリフレッシュトークンと組み合わせる前提で設計します。

JWTを無効化(ログアウト)するにはどうしますか?

JWTはサーバに状態を持たないため個別失効が難しく、基本は有効期限切れを待ちます。即時に失効させたい場合は、アクセス用JWTを短命にしてリフレッシュトークン側をサーバ管理で無効化するか、失効させたいトークンのjtiをブロックリストに載せて検証時に照合する方法をとります。

HS256とRS256はどちらを選べばよいですか?

鍵をどこまで配るかで決まります。発行と検証が同じサービス内で完結するなら共通鍵のHS256が単純です。認証サーバが発行したトークンを別のサービスやフロントで検証するなら、秘密鍵を配らずに済む公開鍵方式のRS256やES256を選びます。検証側に共通鍵を渡すとその鍵で偽造できるため、サービスをまたぐ構成でHS256は避けます。

JWTとOAuth・OIDCの違いは何ですか?

層が異なります。JWTは情報を署名付きで運ぶトークンの器の仕様、OAuth 2.0はアクセス権限を委譲する認可の枠組み、OIDCはOAuth 2.0の上に認証層を足したプロトコルです。OIDCが発行するIDトークンの実体はJWTであり、JWTはこれらのプロトコルの中で使われる部品という関係になります。

JWTの有効期限(exp)はどのくらいに設定しますか?

アクセストークンなら5分から15分に切り、リフレッシュトークンで更新する構成が扱いやすい水準です。盗まれたトークンが有効な時間はそのまま被害の窓になります。短くしすぎると更新の通信が増えるため、更新フローとセットで詰めます。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

ほか 16 件の記事からもリンクされています。

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.09 テックブログ IDCFクラウド(IDCフロンティア)不正アクセス・ランサムウェア:影響先・復旧・データは戻るか
  2. 2026.10.08 テックブログ 大阪公立大学のランサムウェア被害と仮想化基盤の停止|全授業休講に至った経緯とバックアップを守る設定
  3. 2024.11.08 テックブログ OpenAPI GeneratorでJavaコードを自動生成する方法|CLI導入からSpring・ライブラリ選択まで
  4. 2026.10.09 テックブログ ニッスイのサイバー攻撃で日水物流の入出荷停止|委託先クラウド障害に荷主が備える手順
  5. 2026.10.09 テックブログ 京王電鉄のランサムウェア被害とグループ共通基盤:決済・ポイント・予約が止まった範囲と遮断の初動

RELATED POSTS 関連記事

目次