【実務・中級編】 JWTのヘッダー・ペイロード・署名の構造とBase64Urlエンコーディング – Web APIアーキテクチャ・データ連携実践ガイド

JWTの深淵を覗く:JSON Web Tokenの構造とセキュリティのリアル

Web APIの設計において、もはや避けては通れない技術「JWT(JSON Web Token)」。
多くのエンジニアが「認証トークンの一種」としてフワッと理解したまま実装し、そして深夜のトラブルシューティングで泣きを見るケースを数多く見てきました。「なぜトークンが改ざんできないのか」「Base64Urlとは何なのか」。今日は、そんな疑問を抱える現場のエンジニアのために、JWTの裏側を徹底解剖します。

—

1. JWTは「3つのパーツ」が奏でる調和

JWTは、Header、Payload、Signatureという3つのパーツがドット(.)で連結されたシンプルな文字列です。この構造は [RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519) に厳格に定義されています。

Header:メタデータの素顔

Headerは、トークンの「自己紹介」です。JSONをBase64Urlエンコードしたもので、主に以下の2つを持ちます。

  • alg: 署名アルゴリズム(HS256やRS256など)
  • typ: トークンの種類(JWT固定)

Payload:運ばれる真実

Payloadには、ユーザーIDや有効期限(exp)といった「クレーム」を格納します。注意すべきは、ここが暗号化ではなく単なるエンコードである点です。誰でもデコードして中身を読めるため、クレジットカード番号のような機密情報を平文で入れるのは厳禁です。

Signature:信頼の証

これら2つを秘密鍵(または公開鍵)でハッシュ化したものがSignatureです。サーバー側で再計算し、一致しなければ「どこかで誰かがいじったな」と即座に検知できます。

—

2. Base64Urlエンコードの罠

JWTがなぜBase64Urlなのか。それは、URLやHTTPヘッダー(Authorization: Bearer <token>)の中で特別なエスケープ処理をせずにそのまま送れるようにするためです。

標準のBase64との違いは以下の3点。ここを理解していないと、独自でトークン生成ロジックを書いた際に必ずハマります。

1. + を - に置換
2. / を _ に置換
3. 末尾のパディング = を削除

PythonでJWTを自作する際、標準の base64 ライブラリを使うと痛い目を見ます。必ず専用の変換を行うか、PyJWTのような枯れたライブラリを使用してください。

import base64
import json

def base64url_encode(data: dict) -> str:
    # JSONを文字列化してバイト列へ
    json_bytes = json.dumps(data).encode('utf-8')
    # Base64変換し、URLセーフな文字に置換して末尾の=を削除
    return base64.urlsafe_b64encode(json_bytes).decode('utf-8').rstrip('=')

# ヘッダーの例
header = {"alg": "HS256", "typ": "JWT"}
print(base64url_encode(header))

—

3. 署名アルゴリズム:HS256 vs RS256

現場で最も議論になるのが「どのアルゴリズムを選ぶべきか」です。

HS256 (HMAC with SHA-256)

  • 特性: 共通鍵暗号。サーバーとクライアント(またはマイクロサービス間)で同じ鍵を共有します。
  • 用途: シンプルな認証、単一のサービス。
  • リスク: 鍵が漏洩すると全トークンが偽造されます。

RS256 (RSA Signature with SHA-256)

  • 特性: 非対称鍵暗号。秘密鍵で署名し、公開鍵で検証します。
  • 用途: 分散システム、マイクロサービスアーキテクチャ。
  • メリット: トークンを検証するサービス側に秘密鍵を渡す必要がないため、セキュリティ強度が段違いです。迷ったらRS256を選んでください。

—

4. 実践:curlで叩くJWTのデバッグ

APIの挙動がおかしいとき、まずはcurlでヘッダーを確認するのが鉄則です。

# APIサーバーからトークンを取得し、ヘッダーを詳細表示する例
curl -v -X POST https://api.example.com/login \
  -H "Content-Type: application/json" \
  -d '{"username": "admin", "password": "password123"}'

受け取ったトークンの中身が正しいか確認する際は、[jwt.io](https://jwt.io/) も便利ですが、機密性の高い環境では自前で検証スクリプトを持つべきです。

import jwt # PyJWTライブラリ

# 公開鍵を使って検証する例
public_key = """-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----"""

try:
    # トークンを検証(署名チェックと期限チェックを自動実行)
    payload = jwt.decode(token, public_key, algorithms=["RS256"])
    print("検証成功:", payload)
except jwt.ExpiredSignatureError:
    print("トークンの有効期限が切れています")
except jwt.InvalidTokenError:
    print("不正なトークンです")

—

5. 最後に:現場からのアドバイス

JWTは「ステートレス」という強力な武器を持っていますが、その分「一度発行したトークンを即座に無効化する(ブラックリスト管理)」のが難しいという弱点もあります。

大規模なシステムでJWTを導入する際は、必ずexp(有効期限)を短く設定し、必要に応じてRefresh Tokenと組み合わせる設計にしてください。また、署名鍵のローテーション戦略も設計段階で組み込んでおくこと。

ネットワークのパケットを追いかけるように、データの流れと信頼の連鎖を意識する。これが、堅牢なAPIを構築するための唯一の近道です。今日の学びが、あなたの次のアーキテクチャ設計の助けになれば幸いです。

コメント

タイトルとURLをコピーしました