JWTトークンを理解する:完全ガイド

· 12分で読む

目次

JSON Web Token(JWT)とは?

JSON Web Token(JWT)は、現代のWebアプリケーションにおいて、当事者間で情報を安全に送信するための事実上の標準となっています。RFC 7519で定義されているJWTは、2つの当事者間で転送されるクレームを表現するための、コンパクトでURLセーフな方法を提供します。

サーバーが状態を維持する従来のセッションベース認証とは異なり、JWTは自己完結型です。つまり、トークン自体がユーザーの身元と権限を検証するために必要なすべての情報を保持しています。このステートレスな性質により、JWTは分散システム、マイクロサービスアーキテクチャ、モバイルアプリケーションにおいて特に価値があります。

JWTは現代のアプリケーションにおいて2つの主要な目的を果たします:

JWTの美しさは、そのシンプルさと汎用性にあります。異なるプログラミング言語やプラットフォーム間でシームレスに動作するため、フロントエンドがReact、バックエンドがNode.js、モバイルアプリがSwiftやKotlinといった異種環境に最適です。

プロのヒント: JWTは非常に便利ですが、すべての認証シナリオに対する万能薬ではありません。JWTと従来のセッションをいつ使用するかを理解することは、安全でスケーラブルなアプリケーションを構築する上で重要です。

JWTの詳細な構造と解剖

JWTは、ドット(.)で区切られた3つの異なる部分で構成され、xxxxx.yyyyy.zzzzzのような構造を形成します。各セクションは特定の目的を果たし、一緒になって改ざん防止された情報パッケージを作成します。

ヘッダー

ヘッダーは通常、トークンのタイプ(JWT)と使用されている署名アルゴリズム(HMAC SHA256やRSAなど)の2つの部分で構成されます。この情報は、受信側にトークンの署名を検証する方法を伝えます。

{
  "alg": "HS256",
  "typ": "JWT"
}

ヘッダーはBase64Urlエンコードされ、JWTの最初の部分を形成します。よく遭遇する一般的なアルゴリズムには以下があります:

ペイロード

ペイロードには、エンティティ(通常はユーザー)に関するステートメントと追加のメタデータであるクレームが含まれます。クレームは3つのタイプに分類されます:

登録済みクレーム: これらは必須ではありませんが、相互運用性を提供するために推奨される事前定義されたクレームです。以下が含まれます:

公開クレーム: これらはアプリケーション用に定義するカスタムクレームです。衝突を避けるため、IANA JSON Web Tokenレジストリで定義するか、衝突耐性のある名前(名前空間付きURIなど)を使用する必要があります。

プライベートクレーム: 使用に同意した当事者間で情報を共有するために作成されたカスタムクレーム。これらは登録済みクレームでも公開クレームでもありません。

{
  "sub": "1234567890",
  "name": "John Doe",
  "email": "[email protected]",
  "role": "admin",
  "iat": 1516239022,
  "exp": 1516242622
}

ペイロードはBase64Urlエンコードされ、JWTの2番目の部分を形成します。

クイックヒント: パスワードやクレジットカード番号などの機密情報をJWTペイロードに保存しないでください。JWTは署名されていますが、デフォルトでは暗号化されていないため、誰でもペイロードをデコードして読むことができます。

署名

署名は、エンコードされたヘッダー、エンコードされたペイロード、秘密(HMACアルゴリズムの場合)または秘密鍵(RSA/ECDSAの場合)を取り、ヘッダーで指定されたアルゴリズムを使用して署名することで作成されます。

例えば、HMAC SHA256を使用する場合、署名は次のように作成されます:

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

署名は、トークンが変更されていないことを保証します。誰かがヘッダーやペイロードの1文字でも変更すると、署名検証は失敗します。

完全なJWTの例

3つの部分をすべて組み合わせると、次のような完全なJWTが得られます:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

JWTデコーダーツールを使用して、任意のJWTをデコードして検査し、そのヘッダー、ペイロードを確認し、署名を検証できます。

実践におけるJWTの作成と活用

適切なライブラリを使用すれば、JWTの作成は簡単です。さまざまなプログラミング言語とシナリオでJWTを生成して使用する方法を見ていきましょう。

Node.jsでのJWT作成

jsonwebtokenライブラリは、Node.jsアプリケーションで最も人気のある選択肢です:

const jwt = require('jsonwebtoken');

// トークンを作成
const payload = {
  sub: user.id,
  email: user.email,
  role: user.role
};

const secret = process.env.JWT_SECRET;
const options = {
  expiresIn: '1h',
  issuer: 'myapp.com'
};

const token = jwt.sign(payload, secret, options);

// トークンを検証
try {
  const decoded = jwt.verify(token, secret);
  console.log(decoded);
} catch (error) {
  console.error('無効なトークン:', error.message);
}

PythonでのJWT作成

Python開発者は通常、PyJWTライブラリを使用します:

import jwt
import datetime

# トークンを作成
payload = {
    'sub': user.id,
    'email': user.email,
    'role': user.role,
    'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1),
    'iat': datetime.datetime.utcnow()
}

secret = os.environ.get('JWT_SECRET')
token = jwt.encode(payload, secret, algorithm='HS256')

# トークンを検証
try:
    decoded = jwt.decode(token, secret, algorithms=['HS256'])
    print(decoded)
except jwt.ExpiredSignatureError:
    print('トークンの有効期限が切れています')
except jwt.InvalidTokenError:
    print('無効なトークン')

JavaでのJWT作成

Javaアプリケーションでは、Auth0のjava-jwtライブラリが広く使用されています:

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;

// トークンを作成
Algorithm algorithm = Algorithm.HMAC256(secret);
String token = JWT.create()
    .withSubject(user.getId())
    .withClaim("email", user.getEmail())
    .withClaim("role", user.getRole())
    .withExpiresAt(new Date(System.currentTimeMillis() + 3600000))
    .withIssuer("myapp.com")
    .sign(algorithm);

// トークンを検証
try {
    DecodedJWT jwt = JWT.require(algorithm)
        .withIssuer("myapp.com")
        .build()
        .verify(token);
} catch (Exception e) {
    System.out.println("無効なトークン: " + e.getMessage());
}

トークン生成のベストプラクティス

JWTを作成する際は、セキュリティと信頼性を確保するために以下のガイドラインに従ってください:

プロのヒント: リフレッシュトークンを使用したトークンリフレッシュメカニズムの実装を検討してください。これにより、頻繁な再認証なしに良好なユーザーエクスペリエンスを維持しながら、セキュリティのためにアクセストークンを短命に保つことができます。

JWT認証ワークフローの説明

JWTがアプリケーション内でどのように流れるかを理解することは、正しく実装するために重要です。典型的な認証ワークフローをステップバイステップで見ていきましょう。

初期認証

  1. ユーザーが資格情報を送信: ユーザーはユーザー名とパスワードを認証エンドポイント(通常はPOST /api/auth/login)に送信します。
  2. サーバーが資格情報を検証: サーバーはデータベースに対して資格情報をチェックし、パスワードハッシュが一致することを確認します。
  3. サーバーがJWTを生成: 検証が成功すると、サーバーはユーザーの身元と関連するクレームを含むJWTを作成します。
  4. サーバーがトークンを返す: JWTはクライアントに返され、通常はレスポンスボディに含まれます。一部の実装では、HTTP-onlyクッキーとして設定されることもあります。

後続のリクエスト

  1. クライアントがトークンを含める: 後続の各リクエストで、クライアントはAuthorizationヘッダーにJWTを含めます:Authorization: Bearer <token>
  2. サーバーがトークンを検証: サーバーはトークンを抽出し、その署名を検証し、有効期限をチェックします。
  3. サーバーがリクエストを処理: トークンが有効な場合、サーバーはペイロードからユーザー情報を抽出し、それに応じてリクエストを処理します。
  4. サーバーがレスポンスを返す: 要求されたデータまたは操作結果がクライアントに返されます。

トークンリフレッシュフロー

アクセストークンの有効期限が切れると、リフレッシュフローが開始されます:

  1. クライアントが有効期限切れを検出: クライアントは401 Unauthorizedレスポンスを受け取るか、トークンのexpクレームをチェックします。
  2. クライアントがリフレッシュトークンを送信: クライアントは専用のリフレッシュエンドポイント(POST /api/auth/refresh)にリフレッシュトークンを送信します。
  3. サーバーがリフレッシュトークンを検証: サーバーはリフレッシュトークンを検証し、取り消されていないかチェックします。
  4. サーバーが新しいアクセストークンを発行: 新しいアクセストークンが生成され、クライアントに返されます。
  5. クライアントが元のリクエストを再試行: クライアントは新しいアクセストークンを使用して、失敗したリクエストを再試行します。

ログアウトフロー

JWTでのログアウトは、トークンがステートレスであるため、特別な考慮が必要です:

  1. クライアントがログアウトを開始: ユーザーがログアウトをクリックし、POST /api/auth/logoutへのリクエストをトリガーします。
  2. サーバーがリフレッシュトークンを無効化: サーバーはリフレッシュトークンを取り消しリストに追加するか、データベースから削除します。
  3. クライアントがトークンを破棄: クライアントはアクセストークンとリフレッシュトークンの両方をストレージから削除します。
  4. クライアントがリダイレクト: ユーザーはログインページまたは公開エリアにリダイレクトされます。
トークンタイプ 典型的な有効期間 保存場所 目的
アクセストークン 15分 - 1時間 メモリまたはsessionStorage APIリクエストの認可
リフレッシュトークン 7日 - 30日 HTTP-onlyクッキーまたはセキュアストレージ 新しいアクセストークンの取得
IDトークン アクセストークンと同じ メモリ ユーザー識別情報(OpenID Connect)

クイックヒント: クライアントアプリケーションに自動トークンリフレッシュを実装して、トークンの有効期限切れをシームレスに処理します。これにより、ユーザーが予期せずログアウトされることを防ぎます

We use cookies for analytics. By continuing, you agree to our Privacy Policy.