JWK

暗号鍵を JSON 形式で表現する標準仕様で、JWT の署名検証に使用される

認証セキュリティ

JWK とは

JWK (JSON Web Key) は、暗号鍵を JSON 形式で表現する標準仕様 (RFC 7517) である。JWT の署名検証に使う公開鍵を、HTTP エンドポイント経由で安全に配布するために広く使われている。

従来、公開鍵の配布は PEM ファイルの手動コピーや X.509 証明書の配布に依存していた。JWK はこれを JSON + HTTPS という Web ネイティブな方式に置き換え、鍵のローテーションや複数鍵の管理を自動化可能にした。OAuth 2.0 / OpenID Connect のエコシステムで事実上の標準となっている。

JWK の構造

{
  "kty": "RSA",
  "kid": "2026-03-key-1",
  "use": "sig",
  "alg": "RS256",
  "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2...",
  "e": "AQAB"
}
フィールド 意味
kty 鍵の種類 (Key Type) RSA, EC, OKP
kid 鍵の識別子 (Key ID) 任意の文字列。JWT ヘッダーの kid と照合
use 用途 sig (署名), enc (暗号化)
alg アルゴリズム RS256, ES256
n, e RSA 公開鍵のパラメータ Base64url エンコードされた値

kid が最も重要なフィールドだ。JWT を受け取ったサーバーは、JWT ヘッダーの kid と JWKS 内の kid を照合し、どの鍵で署名検証すべきかを特定する。

JWKS エンドポイント

認証プロバイダーは、公開鍵のセットを JWKS (JSON Web Key Set) エンドポイントで公開する。

# Cognito
https://cognito-idp.ap-northeast-1.amazonaws.com/{userPoolId}/.well-known/jwks.json

# Auth0
https://{tenant}.auth0.com/.well-known/jwks.json

# Google
https://www.googleapis.com/oauth2/v3/certs

レスポンスは keys 配列に複数の JWK を含む。

{
  "keys": [
    { "kid": "key-1", "kty": "RSA", "use": "sig", ... },
    { "kid": "key-2", "kty": "RSA", "use": "sig", ... }
  ]
}

複数の鍵が含まれるのは、鍵のローテーション中に新旧両方の鍵が有効である必要があるためだ。

JWT 署名検証の流れ

  1. クライアントが JWT をサーバーに送信する
  2. サーバーは JWT ヘッダーから kid を取得する
  3. JWKS エンドポイントから公開鍵セットを取得する (キャッシュがあればキャッシュから)
  4. kid が一致する JWK を見つける
  5. その公開鍵で JWT の署名を検証する
  6. 署名が有効なら、JWT のペイロード (claims) を信頼する
import jwt from 'jsonwebtoken';
import jwksClient from 'jwks-rsa';

const client = jwksClient({
  jwksUri: `https://cognito-idp.ap-northeast-1.amazonaws.com/${userPoolId}/.well-known/jwks.json`,
  cache: true,           // JWKS をキャッシュ
  cacheMaxAge: 600000,   // 10分間キャッシュ
});

function getKey(header: jwt.JwtHeader, callback: jwt.SigningKeyCallback) {
  client.getSigningKey(header.kid!, (err, key) => {
    callback(err, key?.getPublicKey());
  });
}

jwt.verify(token, getKey, { algorithms: ['RS256'] }, (err, decoded) => {
  if (err) throw new Error('JWT 検証失敗');
  // decoded にペイロードが入る
});

鍵のローテーション

認証プロバイダーは定期的に署名鍵をローテーションする。Cognito は自動的にローテーションを行い、JWKS エンドポイントには常に新旧両方の鍵が含まれる。

ローテーションの流れは以下の通りだ。

  1. 新しい鍵ペア (key-2) を生成し、JWKS に追加する
  2. 新しい JWT は key-2 で署名する
  3. 古い鍵 (key-1) で署名された JWT がまだ有効期限内なので、key-1 も JWKS に残す
  4. key-1 で署名された JWT が全て期限切れになったら、key-1 を JWKS から削除する

クライアント側の実装で重要なのは、JWKS をキャッシュしつつ、未知の kid に遭遇したら JWKS を再取得するロジックだ。キャッシュだけに頼ると、ローテーション直後に検証が失敗する。

API Gateway での JWT 検証

API Gateway (HTTP API) の JWT オーソライザーを使えば、Lambda 側で検証ロジックを実装する必要がない。

API Gateway が自動的に行う処理:

  • JWKS エンドポイントから公開鍵を取得・キャッシュ
  • JWT の署名検証
  • exp (有効期限) のチェック
  • iss (発行者) と aud (対象者) の検証

Lambda には検証済みの claims がイベントオブジェクトに含まれて渡される。これにより、Lambda は認証ロジックを一切持たず、ビジネスロジックに集中できる。

RSA と EC の選択

JWK で使われる鍵の種類は主に RSA と EC (楕円曲線) の 2 つだ。

観点 RSA (RS256) EC (ES256)
鍵サイズ 2048 bit (256 bytes) 256 bit (32 bytes)
署名サイズ 256 bytes 64 bytes
検証速度 速い やや遅い
署名速度 遅い 速い
互換性 ほぼ全てのライブラリが対応 一部の古いライブラリで非対応

Cognito や Auth0 はデフォルトで RSA を使用する。JWT のサイズを小さくしたい場合 (モバイルアプリなど帯域が限られる環境) は EC を検討する価値がある。

よくある誤解

「JWKS エンドポイントは秘密にすべき」

JWKS エンドポイントは公開鍵を配布するためのものであり、秘密にする必要はない。公開鍵は文字通り「公開」するための鍵だ。秘密にすべきなのは署名に使う秘密鍵であり、これは認証プロバイダーの内部に保持される。

「JWT を検証すれば認可も完了」

JWT の署名検証は認証 (このトークンは本物か) を確認するだけだ。認可 (このユーザーにこの操作を許可するか) は、JWT のペイロードに含まれる claims (roles, scopes) を基に、アプリケーション側で別途判断する必要がある。

体系的に学ぶなら関連書籍を参照してほしい。

関連用語