JWTの構造を解説 — ヘッダー・ペイロード・署名の役割とは

JWTは一見ランダムな文字列に見えますが、実際は3つの部分に分けて考えれば構造はとてもシンプルです。

ドットで区切られた3つの部分

JWTは「ヘッダー.ペイロード.署名」という、ピリオドで区切られた3つのBase64urlエンコード済みの部分でできています。ヘッダーはトークンの種類と署名アルゴリズムを、ペイロードは実際のクレーム(情報)を持ち、署名は前の2つが改ざんされていないことを保証します。

暗号化ではなくエンコード(符号化)

Base64urlエンコードは誰でも鍵なしで元に戻せる可逆的な処理で、URLやJSONで扱いやすいバイナリセーフな形式にするためのものであり、内容を隠すためのものではありません。標準的なJWTのヘッダーとペイロードは、基本的なデコードツールに貼り付けるだけで誰でも読めます。

ペイロードに機密情報を入れてはいけない

ペイロードはトークンを持つ人なら誰でもそのまま読めるため、パスワードや個人の機微情報のような機密データを標準的なJWTのクレームに含めるべきではありません。ペイロードは非公開情報ではなく、可視情報として扱う必要があります。

署名が保証するのは「改ざんされていないこと」

署名によってサーバーは、そのトークンが信頼できる発行元から発行され、発行後に改ざんされていないことを検証できますが、トークンを傍受した第三者からペイロードの内容を隠すものではありません。検証には署名用の鍵が必要ですが、デコード自体には鍵は不要です。

よく登場する標準クレーム

exp(有効期限)とiat(発行時刻)はUnixタイムスタンプでトークンの有効期間を制御し、sub(主体)は通常ユーザーを識別し、iss(発行者)はトークンを作成した発行元を示します。アプリケーションはこれらに加えて独自のクレームを追加できます。

JWTが認証で広く使われるようになった理由

JWTを使えば、リクエストのたびにデータベースでセッション情報を照会しなくても、トークン自体が持つ検証可能なクレームと有効期限だけでサーバー側は検証できます。このステートレスな性質は分散システムやAPIとの相性が良い一方、従来のサーバー側セッションに比べて即座に無効化しにくいというトレードオフがあります。

デコードと検証はまったく別の処理

JWTのヘッダーとペイロードは鍵がなくても誰でもデコードできます。それが本物で改ざんされていないことを検証するには、発行時に使われた署名鍵や公開鍵が必要です。デコードしただけで検証していないトークンの内容を、何かの証明として信頼してはいけません。

よくある質問

パスワードなしでJWTのペイロードが読めてしまうのはセキュリティ上の欠陥ですか?

いいえ、それは設計上想定された挙動です。JWTは読めることが前提の仕組みで、提供しているセキュリティ上の保証は署名による改ざん検知(完全性)であり、ペイロード内容の機密性ではありません。

JWTのペイロードを書き換えても使えますか?

デコードしたテキスト自体は書き換えられますが、正しい署名鍵なしに再エンコードすると署名が無効になるため、トークンを正しく検証するサーバーは改ざんされたクレームを受け入れず拒否します。