JWTのデコードと署名検証の違い|exp・iatの読み方
2026.10.03 更新 · 4分で読めます
JWTのデコードはBase64URLを読むだけで、署名の検証ではありません。ヘッダーとペイロードの構造、exp・iat・nbfの時刻の読み方、alg: noneのトークンを受け入れてはいけない理由をRFC 7519をもとにまとめました。
JWTデコーダーをすぐに使うデコードは署名の検証ではありません。JWTの内容は誰でも読めるため、秘密情報を入れないでください。JWTデコーダーにトークンを貼り付けると、ヘッダーとペイロード、有効期限を確認できますが、それは読んでいるだけです。そのトークンを信頼してよいかどうかは、受け取ったサーバーが鍵で署名を検証し、expやaudなどのクレームを確認して初めて決まります。
JWTは3つの部分でできている
署名付きのJWTはヘッダー.ペイロード.署名という形で、3つの部分はそれぞれパディングなしのBase64URLで書かれています(RFC 7515)。例として、HS256アルゴリズムと例示用の秘密鍵zekilo-secretで署名したトークンを見てみます。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlpFS0lMTyIsImlhdCI6MTc2NzIyNTYwMCwiZXhwIjoxNzY3MjI5MjAwfQ.218DR4r7DRNkKO3XJcQYXE0ztwbqxYMeQpkIp9S18oE
| 部分 | デコードした内容 |
|---|---|
| ヘッダー | {"alg":"HS256","typ":"JWT"} |
| ペイロード | {"sub":"1234567890","name":"ZEKILO","iat":1767225600,"exp":1767229200} |
| 署名 | 32バイトのHMAC-SHA256の値(Base64URLで43文字) |
ヘッダーとペイロードは暗号化されていません。Base64URLで書かれたJSONなので、鍵がなくても読めます。内容まで暗号化したJWTはJWEという別の形式で、ドットで区切った部分が5つあります。
デコードと検証の違い
| 項目 | デコード | 署名検証 |
|---|---|---|
| 行うこと | Base64URLを戻してJSONを読む | 鍵を使って署名を確かめる |
| 必要なもの | なし | 秘密鍵(HS256)または公開鍵(RS256・ES256) |
| わかること | トークンに書かれている内容 | 内容が、鍵を持つ側が署名したままであること |
| できる人 | 誰でも | 鍵を持つ側 |
署名の対象は、BASE64URL(ヘッダー)、ドット、BASE64URL(ペイロード)をつなげた文字列です(RFC 7515)。上のトークンの前半2つの部分についてzekilo-secretでHMAC-SHA256を計算すると、3つ目の部分と同じ値になります。ペイロードが1文字でも違えば計算結果も変わるため、検証する側は内容が変えられたことに気づけます。
検証は署名の確認だけでは終わりません。受け取る側は、有効期限(exp)、発行者(iss)、対象(aud)も確認する必要があります。RFC 7519は、audクレームがあるのにその値に自分が含まれていない場合、トークンを拒否しなければならないと定めています。
exp・iat・nbfの読み方
この3つのクレームの値はNumericDate、つまり1970-01-01T00:00:00Zからの秒数です(RFC 7519 2節)。ミリ秒ではありません。JavaScriptのDate.now()はミリ秒を返すので、1000で割ってから比較します。
| クレーム | 意味 | 例のトークンの値 | 日時 |
|---|---|---|---|
iat |
発行した時刻 | 1767225600 | 2026-01-01 00:00:00 UTC(JST 09:00) |
exp |
この時刻以降は受け入れてはならない | 1767229200 | 2026-01-01 01:00:00 UTC(JST 10:00) |
nbf |
この時刻より前は受け入れてはならない | なし | — |
expからiatを引くと3,600秒なので、このトークンの有効期間は発行から1時間です。RFC 7519は、サーバー間の時計のずれを考慮して、通常は数分以内の猶予を設けてもよいとしています。登録済みクレームはどれも任意なので、トークンに入っていないこともあります。
数値を日時に直すときは、UNIX時間変換に1767225600を入力すると、2026-01-01T00:00:00Z、日本時間では2026-01-01 09:00:00と表示されます。JWTデコーダーもexp・nbf・iatの横に日時を表示し、expを過ぎていれば期限切れの警告を出します。この表示はブラウザの時計と比べた参考情報で、検証の代わりにはなりません。
alg: noneとアルゴリズムの確認
RFC 7519 6節は、署名のないJWT(Unsecured JWT)を定義しています。ヘッダーのalgがnoneで、署名の部分が空になっています。別の手段で保護されている場合のための形式であり、通常の認証トークンとして受け入れるものではありません。
- RFC 7518 3.6節:実装は、署名のないトークンを既定で受け入れてはなりません。
- RFC 8725 3.1節:ライブラリは、呼び出し側が許可するアルゴリズムの一覧を指定できるようにし、それ以外のアルゴリズムを使ってはなりません。
したがってサーバーは、受け入れるアルゴリズムを自分のコードで決めておき、トークンのヘッダーに書かれたalgにそのまま従わないようにします。JWTデコーダーは、algがnoneのトークンに署名がないという警告を表示します。
実務で守ること
- ペイロードにパスワード、マイナンバー、カード番号などの情報を入れません。
- トークンは、持っている人が使える通行証です。本番環境のトークンをチャット、課題管理ツール、ログに貼り付けません。
- 検証は、サーバー側で実績のあるライブラリを使って行います。アルゴリズムを決めておき、署名と
exp・nbf・iss・audを確認します。 - クライアントでデコードした値は、画面表示だけに使います。権限の判断は、サーバーが検証した結果で行います。