すべての記事

JWT:トークンの構造と「デコード」の意味

JWT(JSON Web Token)は署名済みデータを送るためのコンパクトな形式で、多くの場合、認証済みユーザーに関する情報を表します。トークンはドット区切りの3つの部分から成ります:header.payload.signature。日本のエンタープライズ開発ではSpring BootやJavaベースの社内システムでの採用例が多く、フロントはVue.jsやReactという組み合わせもよく見られます。

payloadに日本語を含めると何が起きるか

{"name": "山田太郎", "city": "大阪"}のようなpayloadも、他のJSONと同様にBase64URLでエンコードされます——この処理はUTF-8のバイト列に対して行われるだけで、日本語かどうかは関係ありません。実務上のポイントは、漢字・ひらがな・カタカナがUTF-8で1文字あたり3バイトを占めるため、日本語の氏名や住所を含むpayloadは、同じ意味の英語版よりもエンコード後のトークンが目に見えて長くなるという点です。

トークンの3つの部分

  • Header — トークンの種類と署名アルゴリズム(HS256やRS256など)を含むJSONで、Base64URLでエンコードされています。
  • Payload — ユーザーデータ、発行時刻、有効期限などの「claim」を含むJSONで、これもBase64URLでエンコードされています。
  • Signature — 秘密鍵または秘密鍵を使ってheaderとpayloadから計算された署名で、トークンが改ざんされていないことを保証します。

通常のBase64ではなくBase64URL

JWTはURLセーフなアルファベットを使うBase64の変種を採用しています。+/-_に置き換えられ、パディングの=は通常省略されます。これにより、追加のエンコードなしでトークンをURLやヘッダーに埋め込めます。

重要な違い:デコードと検証は別物

headerとpayloadは単なるBase64URLです——誰でも鍵なしでデコードして内容を読み取れます。トークンをデコードしても、データが改ざんされていないことの証明にはなりません。トークンの内容を信頼できるのは、対応する鍵で署名を検証した後だけであり、その検証を行うのはサーバー側です。

危険な攻撃: アルゴリズムを「none」にすり替える

JWTの仕様ではnoneアルゴリズム——つまり署名なしのトークン——が許容されています。バックエンドが固定の既知のアルゴリズムで署名を検証する代わりに、トークンのheaderに含まれるalgフィールドを安易に信頼していると、攻撃者はalgnoneにすり替えて署名を取り除くことができます。信頼できるJWTライブラリは、検証時に想定するアルゴリズムを明示的に指定することを要求します。

マイナンバー連携システムでの注意点

行政系や金融系のシステムでJWTを扱う場合、payloadに含める情報の範囲には特に注意が必要です。マイナンバーや口座情報のような機微情報をそのままclaimに入れてしまうと、署名検証をパスした正規のトークンであっても、デコードするだけで誰でも中身を読める以上、実質的に平文で個人情報を配布しているのと同じ状態になります。

ツールを試す