JSON Web TokenにおけるBase64url

JWTがヘッダーとペイロードにBase64urlエンコードを使う仕組みを解説。JWTセグメントのデコード方法、パディングルール、よくあるJWTの誤りを紹介します。

Format

詳細な説明

JSON Web Token(JWT)は、ヘッダーとペイロードのセグメントに標準Base64ではなくBase64urlエンコードを使用します。このエンコードを理解することは、JWTベースの認証のデバッグ、検査、実装に不可欠です。

JWTの構造: JWTはドットで区切られた3つのパートで構成されます:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
  • ヘッダー(最初のセグメント):アルゴリズムとトークンタイプを指定するBase64urlエンコードされたJSON
  • ペイロード(2番目のセグメント):クレーム(ユーザーデータ、有効期限など)を含むBase64urlエンコードされたJSON
  • 署名(3番目のセグメント):ヘッダーとペイロードに対する暗号署名

なぜ標準Base64ではなくBase64urlなのか? JWTはURL内(クエリパラメータ、リダイレクトURI)やHTTPヘッダーに頻繁に含まれます。標準Base64の +/= 文字は、これらのコンテキストでパース上の問題を引き起こします。Base64urlはURLセーフな代替文字に置き換えることでこの問題を解決します。

JavaScriptでのJWTデコード:

function decodeJwtPayload(token) {
  const payload = token.split(".")[1];
  // Convert Base64url to Base64
  const base64 = payload.replace(/-/g, "+").replace(/_/g, "/");
  // Add padding
  const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
  // Decode
  return JSON.parse(atob(padded));
}

重要なニュアンス:

  • JWTのBase64urlセグメントにはパディング(=)が含まれません。RFC 7515仕様では、パディングを省略しなければならないと明示されています。
  • 署名セグメントもBase64urlエンコードされていますが、デコードすると生のバイト列(JSONではない)が得られます。検査のためにデコードするのはヘッダーとペイロードのセグメントのみです。
  • JWTはデフォルトでは署名されているだけで、暗号化されていません。Base64urlエンコードは暗号化ではありません。誰でもJWTのペイロードをデコードして読むことができます。ペイロードに隠すべき機密データが含まれる場合はJWE(JSON Web Encryption)を使用してください。

よくある間違い:

  • Base64urlから標準Base64に変換せずにJWTセグメントに対して直接 atob() を使用すること。文字化けした出力やエラーが発生します。
  • デコードされたペイロードが常に有効なJSONであると仮定すること。不正に作成されたり改ざんされたトークンは、無効なJSONを生成する可能性があります。
  • 署名を検証せずにJWTをデコードしてその内容を信頼すること。デコードは簡単ですが、検証がセキュリティの要です。

ユースケース

OAuth 2.0やOpenID Connectフローのデバッグのために、認証トークンのヘッダーとペイロードのクレームをデコードして表示するJWTインスペクターツールを構築する場合に使用します。

試してみる — Base64 Encoder

フルツールを開く