コンテンツにスキップ

JWT(JSON Web Token)解説

原文

JWT

JSON Web Token (JWT) は、クライアントとサーバー間で情報を安全に送信する方法です。ウェブアプリケーションやAPIで使用され、ユーザーの認証を行い、未承認のアクセスを防ぎます。JWTがトークンを生成する際の構造は、ヘッダー、ペイロード、シグネチャーです。

要約

JWTはクライアント・サーバー間で情報を安全に送受信するためのトークン形式。3パートで構成:ヘッダー(アルゴリズム情報)、ペイロード(クレーム/ユーザー情報)、シグネチャー(改ざん検知)。サーバーはセッション状態を持たずに認証できるためスケールしやすい。

解説

JWTの構造

JWTは . で区切られた3つのBase64URLエンコードされた文字列で構成される。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9   ← ヘッダー
.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c   ← シグネチャー

① ヘッダー (Header)

{
  "alg": "HS256",   // 署名アルゴリズム(HS256, RS256 等)
  "typ": "JWT"
}

② ペイロード (Payload)

クレーム(主張)と呼ばれるキー・バリューペアを格納する。

種別 説明
登録クレーム iss, sub, exp, iat RFC 7519で定義された標準クレーム
パブリッククレーム name, email 公開用ユーザー情報
プライベートクレーム role, tenant_id アプリ独自のクレーム
{
  "sub": "user_123",
  "name": "John Doe",
  "role": "admin",
  "iat": 1516239022,
  "exp": 1516242622
}

③ シグネチャー (Signature)

HMACSHA256(
  base64url(header) + "." + base64url(payload),
  secret
)

シグネチャーにより、トークンが改ざんされていないことを検証できる。ただし、ペイロードはBase64URLエンコードされているだけで暗号化はされていない(JWEを使えば暗号化可能)。

認証フロー

クライアント  →  POST /login (email, password)  →  サーバー
           ←  JWT (access_token, refresh_token)  ←

クライアント  →  GET /api/data (Authorization: Bearer <JWT>)  →  サーバー
           ←  サーバーはJWTを検証してレスポンス  ←

JWTのメリット・デメリット

項目 内容
メリット ステートレス(サーバーにセッション不要)
スケールしやすい(DBルックアップ不要)
クロスドメイン対応(CORS対策が簡単)
自己完結型(ペイロードに情報を持てる)
デメリット トークンの無効化が困難(有効期限まで有効)
ペイロードが大きくなると通信量増加
シークレットキー漏洩で全トークンが危険に

セキュリティのベストプラクティス

  • expを必ず設定する: access tokenは短め(15分〜1時間)、refresh tokenは長め(7〜30日)
  • ペイロードに機密情報を入れない: パスワード、クレカ番号等はNG
  • アルゴリズムは明示的に検証する: alg: none 攻撃への対策
  • refresh tokenの失効管理: ローテーションとDB管理でセキュリティ強化
  • HTTPS必須: トークンの盗聴を防ぐ

Goでの実装例

import "github.com/golang-jwt/jwt/v5"

// トークン生成
claims := jwt.MapClaims{
    "sub": userID,
    "exp": time.Now().Add(15 * time.Minute).Unix(),
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
signedToken, err := token.SignedString([]byte(secretKey))

// トークン検証
parsed, err := jwt.Parse(signedToken, func(t *jwt.Token) (interface{}, error) {
    if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
        return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
    }
    return []byte(secretKey), nil
})

→ 関連: 認証と認可の違いデバイストークンスコープ設計

リンク