メインコンテンツへスキップ

← ブログ一覧へ

JWT とは?JSON Web Token の仕組み(と読み方)

ログイン機能を作ったりデバッグしたことがあれば、JWT に出会っているはずです —— クライアントとサーバーの間を行き来し、「誰であるか」を証明するコンパクトなトークンです。仕組みと安全な読み方を説明します。

鍵なしで自分でデコードできます。 JWT 公式ドキュメントの見本トークンを私たちの JWT デコーダーに貼ると、読める 2 つの部分が返ってきます。ヘッダー { "alg": "HS256", "typ": "JWT" } とペイロード { "sub": "1234567890", "name": "John Doe", "iat": 1516239022 } —— 秘密は一切与えていません。JWT について理解すべき最も重要な点がこれです。クレームは平文で流れます。署名が証明するのは「鍵を持つ者が発行し、その後改変されていない」ことで、機密性とはまったく別の保証です。私たちのデコーダーはブラウザ内で動くので、実際のトークンを貼っても端末から出ません。

3 つの部分

JWT はドットで連結された 3 つの Base64URL 文字列です:header.payload.signature

  • ヘッダー —— メタデータ、主に署名アルゴリズム。例:{"alg":"HS256","typ":"JWT"}
  • ペイロード —— クレーム、ユーザーやトークンに関する JSON オブジェクト。
  • 署名 —— ヘッダーとペイロードに対する暗号署名。秘密鍵(または秘密)で作られ、改ざん検出に使われます。

JWT デコーダーで前の 2 つを分割して読めます。

よくあるクレーム

ペイロードにはクレームが入り、一部は標準化されています:

  • sub —— サブジェクト(通常はユーザー ID)。
  • iss —— 発行者(誰がトークンを作ったか)。
  • iat —— 発行時刻(Unix タイムスタンプ)。
  • exp —— 有効期限(Unix タイムスタンプ)。これを過ぎたトークンは拒否すべきです。
  • aud —— 想定される受信者。

さらにアプリが追加する独自クレーム(ロール、メールなど)。

JWT の認証での使われ方

典型的なステートレスフロー:ログインするとサーバーが署名付き JWT を返し、クライアントは毎リクエストでそれを送ります(通常 Authorization: Bearer … ヘッダー)。サーバーは署名を検証exp を確認 —— 有効なら、データベース参照なしにクレームを信頼します。このステートレス性が主な魅力です。

署名であって、通常は暗号化ではない

多くの JWT は署名(JWS)であり、暗号化ではありません。署名は完全性と真正性を証明しますが、ペイロードは Base64URL エンコードにすぎず 誰でも読めます。したがって:

  • ペイロードに機密を入れない(パスワード、API キー、個人情報)。
  • 暗号化は別物(JWE)で、はるかに稀です。

JWT の混乱の大半はここで解けます。そして出力によって解けます。JWT 仕様書のサンプルトークンを私たちの JWT デコーダーに貼り、鍵は一切与えませんでした。返ってきたヘッダーは

{
"alg": "HS256",
"typ": "JWT"
}

ペイロードは

{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}

復号されたものは何もありません。暗号化されたものが何もないからです。この 2 つのセグメントは Base64url のテキストで、署名が守るのは改変からで、閲覧からではありません。トークンを持つ者は中のすべてのクレームを読めます。だから JWT には、URL に入れたくないものを絶対に載せてはいけません。

重要なセキュリティルール

  • 必ずサーバー側で秘密鍵/公開鍵を使って署名を検証する。 デコード(本ツールが行うこと)は検証ではありません。
  • exp を確認する。 期限切れトークンは拒否しなければなりません。
  • HS256 vs RS256: HS256 は 1 つの共有秘密、RS256 は秘密鍵で署名・公開鍵で検証 —— 複数のサービスが検証するが発行はしない場合に適します。
  • 本物の秘密鍵をオンラインツールに貼らない。 クレーム確認のためのデコードは問題ありませんが、検証には鍵が必要で、自分のコードで行うべきです。

トークンを読む

認証の問題をデバッグするには —— トークンは期限切れ?ロールは正しい? —— デコードしてペイロードを確認します。無料の JWT デコーダーはヘッダーとペイロードを表示し、exp を読める時刻に変換します。すべてブラウザ内で行われ、トークンは送信されません。

これは「何をログに書けるか」という実務的な問いも片付けます。{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022 } は出力しても安全です。トークンを持つ人にはすでに読めるからです。しかし 3 番目のセグメントは違います。前 2 つが改変されていないことを証明できる唯一の部分であり、完全なトークンをバグ報告に貼るのは、有効期限が切れるまで使える資格情報を渡すことです。

デコード例

この広く使われる例のトークンを見てみましょう:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

前の 2 つをデコードすると次のようになります:

// header
{ "alg": "HS256", "typ": "JWT" }
// payload
{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }

3 つ目は署名です。これは「読む」ものではなく、前の 2 つが改ざんされていないことを証明するだけです。

HS256 と RS256 の早見

HS256RS256
系統HMAC-SHA256(対称)RSA-SHA256(非対称)
1 つの共有秘密秘密鍵で署名・公開鍵で検証
有効なトークンを作れる人秘密を持つ全員秘密鍵の保持者だけ
向く場面1 つのサービスが発行も検証も行う多数が検証し、1 つが発行する

exp と iat の扱い

expiatUnix タイムスタンプ —— 1970-01-01 UTC からの「秒」数で、ミリ秒ではありません。上の 1516239022 は 2018-01-18 です。Unix タイムスタンプ変換ツールで変換でき、数字に馴染みがなければ Unix タイムスタンプ入門を参照してください。

もう 2 つのセキュリティの落とし穴

  • alg: none を拒否すること、そして期待するアルゴリズムを固定すること —— さもないと攻撃者が署名を外したり、RS256 を HS256 に格下げできます。
  • JWT は exp より前に失効させるのが難しい。 有効期間は短くし、機密操作にはリフレッシュトークンを使いましょう。

覚えておいてください。本サイトのデコーダーは「デコード」するだけで、決して検証しません。サーバーが署名を検証するまで、「デコード済みだが未検証」のペイロードは常に信頼できないものとして扱いましょう。