セキュリティに関する考慮事項
トークン検証が重要な理由
リソースサーバーの主要なセキュリティ責任は、すべての受信リクエストが信頼できる認可サーバーによって発行された有効で改ざんされていないアクセストークンを持っていることを確認することです。トークン検証の失敗は以下につながる可能性があります:
- 不正アクセス — 有効な資格情報なしに攻撃者が保護されたリソースにアクセスする
- 権限昇格 — 偽造されたスコープを持つトークンが昇格された権限を付与する
- トークンリプレイ — 盗まれたトークンが異なるコンテキストから再利用される
- データ流出 — 侵害されたトークンを使用して機密データが抽出される
@authrim/serverは多層防御の原則に基づいて設計されています。すべての検証ステップは、既知の攻撃ベクトルに対して耐性を持つように実装されています。
組み込みセキュリティ機能
SDKにはデフォルトで以下のセキュリティ対策が含まれています。これらの保護は常にアクティブであり、誤って無効にすることはできません。
1. タイミングセーフ比較
セキュリティ上重要なすべての文字列比較は、タイミングサイドチャネル攻撃を防止するために定時間アルゴリズムを使用します:
- 発行者(
iss)の検証 — 攻撃者がどの発行者が信頼されているかを探ることを防止 - オーディエンス(
aud)の検証 — 受け入れられるオーディエンス値の探索を防止 - DPoP Thumbprintバインディング — 期待されるThumbprint値の推測を防止
タイミングセーフ比較により、2つの文字列を比較するのにかかる時間が一致する文字数を明らかにしないことが保証され、情報漏洩が排除されます。
2. alg: noneの拒否
SDKは、ヘッダーにalg: noneを持つJWTを拒否し、InsecureAlgorithmErrorをスローします。「none」アルゴリズムはトークンが未署名であることを意味し、これを受け入れると誰でもトークンを偽造できてしまいます。
// The SDK automatically rejects this — no configuration needed// Header: { "alg": "none", "typ": "JWT" }// Result: InsecureAlgorithmErrorこの保護は無効にできません。未署名トークンを許可する設定オプションはありません。
3. HTTPSの強制
デフォルトでは、requireHttps: trueによりすべての外部URLがHTTPSを使用することが保証されます:
- 発行者URL
- JWKSエンドポイント
- イントロスペクションエンドポイント
- 失効エンドポイント
HTTP URLは、ネットワークリクエストが行われる前の設定時に拒否されます。
4. SSRF対策(JWKSリダイレクトブロック)
JWKSを取得する際、SDKはクロスオリジンリダイレクトをブロックします。これにより、悪意のある認可サーバーがJWKSフェッチを内部ネットワークリソースにリダイレクトするServer-Side Request Forgery(SSRF)攻撃を防止します。
JWKSエンドポイントが異なるオリジンへのリダイレクトを返した場合、SDKはリダイレクトに従う代わりにエラーをスローします。
5. JWTサイズ制限(8 KB)
SDKはサービス拒否攻撃を防止するために8 KBを超えるJWTを拒否します。過大なトークンは以下に使用される可能性があります:
- Base64デコード時のメモリ枯渇
- 署名検証時の過度なCPU使用
- リクエスト処理の遅延
正当なアクセストークンは通常2 KB未満です。
6. シングルフライトJWKSフェッチ
複数のリクエストが同時に到着してJWKSリフレッシュをトリガーした場合、SDKはそれらを単一のネットワークリクエストに統合します。この「シングルフライト」または「サンダリングハード」防止により:
- 認可サーバーへの並列JWKSリクエストの過負荷を回避
- 同時検証間で一貫した鍵の状態を確保
- 待機中のすべてのリクエストのレイテンシを削減
7. 公開鍵のみのJWKインポート
DPoPプルーフを処理する際、SDKはプルーフヘッダーのJWKから公開鍵パラメータのみをインポートします。秘密鍵パラメータ(d、p、q、dp、dq、qi)は拒否されます。
これにより以下を防止します:
- 攻撃者がプルーフに秘密鍵を埋め込んでサーバーを騙すこと
- ログやエラーメッセージを通じた偶発的な秘密鍵の漏洩
8. exp / nbf / iatのクロック許容値
clockToleranceSeconds設定(デフォルト: 60秒)は、認可サーバーとリソースサーバー間のクロックスキューに対するバッファを提供します。SDKはこの許容値を以下に適用します:
exp(有効期限) — 有効期限からclockToleranceSeconds経過するまでのトークンを受け入れnbf(有効開始時刻) — 有効開始時刻のclockToleranceSeconds前までのトークンを受け入れiat(発行時刻) —clockToleranceSeconds以上未来に発行されたトークンを拒否
9. リクエストボディ消費の保護
フレームワークミドルウェアアダプタは、アプリケーションがボディを読み取る前にボディを消費してしまうことを避けるため、リクエストボディの解析を慎重に処理します。Authorizationヘッダーからのトークン抽出はヘッダーのみを対象としており、ボディ処理に干渉しません。
10. DPoPにおける秘密鍵漏洩の防止
SDKはDPoP検証に秘密鍵を要求も処理もしません。リソースサーバーは、プルーフの署名を検証してJWK Thumbprintを計算するために、DPoPプルーフヘッダーの公開鍵のみを必要とします。
やってはいけないことチェックリスト
クライアント設計ガイダンス
コンフィデンシャルクライアントを作成すべき場合
@authrim/serverの一部の機能にはclientCredentials(クライアントIDとシークレット)が必要です。コンフィデンシャルクライアントが必要になるのは以下の場合です:
| 機能 | clientCredentialsが必要 | 理由 |
|---|---|---|
| トークン検証(JWT) | いいえ | 公開鍵を使用してローカルで検証 |
| DPoP検証 | いいえ | プルーフの署名を使用してローカルで検証 |
| トークンイントロスペクション | はい | 認可サーバーが呼び出し元を認証する |
| トークン失効 | はい | 認可サーバーが呼び出し元を認証する |
JWTアクセストークンの検証のみを行う場合、クライアント資格情報は不要です。イントロスペクションまたは失効が必要な場合にのみ、コンフィデンシャルクライアントを登録してください。
// For JWT validation only — no credentials neededconst authrim = new AuthrimServer({ issuer: 'https://auth.example.com', audience: 'https://api.example.com',});
// For introspection/revocation — credentials requiredconst authrim = new AuthrimServer({ issuer: 'https://auth.example.com', audience: 'https://api.example.com', introspectionEndpoint: 'https://auth.example.com/oauth/introspect', revocationEndpoint: 'https://auth.example.com/oauth/revoke', clientCredentials: { clientId: 'my-resource-server', clientSecret: 'resource-server-secret', },});オーディエンスとスコープの設計パターン
オーディエンスとスコープの値は慎重に設計してください:
- APIごとに1つのオーディエンス — 各APIサービスに一意のオーディエンスURIを使用します(例:
https://api.example.com、https://billing.example.com) - スコープの粒度 — 広い
read/writeではなく、read:users、write:orders、admin:settingsのようなスコープを使用します - 最小権限 — クライアントアプリケーションは必要なスコープのみをリクエストすべきです
トークンの有効期間に関する推奨事項
アプリケーションの責任
SDKはほとんどのセキュリティ上の懸念を自動的に処理しますが、一部の責任はアプリケーション側に残ります。
DPoP jtiリプレイ保護
SDKはDPoPプルーフの構造、署名、バインディングを検証しますが、リプレイ検出のためのjti値の追跡は行いません。これは、リプレイ保護にはサーバーサイドの状態(最近見たjti値のキャッシュ)が必要であり、デプロイメントによって異なるためです。
バックチャネルログアウトのjti検証
同様に、BackChannelLogoutValidatorはログアウトトークンの構造とクレームを検証しますが、jti値の追跡は行いません。アプリケーションでは以下を行う必要があります:
- JWT署名を検証する(バリデータが解析済みトークンを提供します)
jtiが以前に使用されていないことを確認する- 対応するユーザーセッションを無効化する
エラーレスポンスのフォーマット
SDKは型付きエラー(例: TokenExpiredError、InvalidSignatureError)をスローしますが、HTTPエラーレスポンスのフォーマットは行いません。アプリケーションまたはミドルウェアアダプタがこれを処理します:
// Example: Express error handlerapp.use((err, req, res, next) => { if (err.name === 'TokenExpiredError') { return res.status(401).json({ error: 'token_expired' }); } if (err.name === 'InsufficientScopeError') { return res.status(403).json({ error: 'insufficient_scope' }); } next(err);});本番チェックリスト
本番環境にデプロイする前に、以下を確認してください。
インフラストラクチャ
- HTTPSが有効 — すべてのエンドポイント(API、発行者、JWKS)がHTTPSを使用。
requireHttpsがtrue(デフォルト)。 - クロック同期 — サーバーがNTPを使用してクロックを同期。
clockToleranceSecondsを超えるクロックドリフトは誤ったトークン拒否を引き起こします。 - DNS解決 — 発行者のホスト名が正しく解決され、DNSスプーフィングに脆弱でないこと。
設定
- 発行者URLが正しい — トークンの
issクレームと正確に一致(末尾スラッシュの動作を含む)。 - オーディエンス値が正しい — トークンの
audクレームと一致。 - クロック許容値が妥当 — デフォルト60秒。300秒を超えないこと。
- JWKSキャッシュTTLが適切 — デフォルト1時間(
jwksRefreshIntervalMs: 3600000)。鍵ローテーションの頻度に基づいて調整。
アプリケーションコード
- トークンがログに記録されていない — 生のトークン文字列の代わりに
sub、jti、scope、iatをログに記録。 - エラーレスポンスが内部詳細を漏洩していない — クライアントには汎用的なエラーコードを返し、詳細はサーバーサイドでログに記録。
- エンドポイントごとにスコープが適用されている — すべての保護されたエンドポイントが必要なスコープを指定。
- DPoP jtiリプレイ保護が実装されている — DPoPを使用する場合、キャッシュ(例: Redis)で
jti値を追跡。 - バックチャネルログアウトが処理されている — バックチャネルログアウトを使用する場合、
jtiの追跡とセッション無効化を実装。
鍵ローテーション
- 鍵ローテーション戦略が定義されている — 認可サーバーが署名鍵を定期的にローテーション。
- JWKSキャッシュがローテーションに対応 — SDKは
kidが見つからない場合に自動的に再試行しますが、環境で動作することを確認。 - 古い鍵が削除されている — 猶予期間後、侵害された鍵の使用を防止するために古い鍵をJWKSから削除。
モニタリング
- トークン検証の失敗がログに記録されている — 攻撃を示す可能性のあるスパイクを監視。
- JWKSフェッチの失敗がアラートされている — SDKが鍵を取得できない場合、すべてのトークン検証が失敗します。
- クロックドリフトが監視されている — NTP同期が失敗した場合にアラート。
脅威モデルの概要
以下の表は、一般的な攻撃とSDKの組み込み防御のマッピングを示しています:
| 脅威 | 攻撃ベクトル | SDKの防御 |
|---|---|---|
| トークン偽造 | alg: noneを使用した偽造JWT | InsecureAlgorithmErrorでalg: noneを拒否 |
| トークン偽造 | 不正な鍵で署名されたJWT | JWKS kidマッチングによる署名検証 |
| トークンリプレイ | 盗まれたBearerトークン | DPoP送信者制約(アプリケーションレベルのjti追跡) |
| タイミング攻撃 | 発行者/オーディエンス値の探索 | タイミングセーフ文字列比較 |
| SSRF | 悪意のあるJWKSリダイレクト | クロスオリジンリダイレクトブロック |
| DoS | 過大なJWTペイロード | 8 KBサイズ制限 |
| サンダリングハード | 同時JWKSリフェッチ | シングルフライトパターン |
| 鍵の混乱 | DPoPプルーフ内の秘密鍵 | 公開鍵のみのインポート |
| 期限切れトークン | クロックスキューの悪用 | 上限付きの設定可能なクロック許容値 |
| 中間者攻撃 | HTTP経由のトークン傍受 | HTTPSの強制(設定可能) |
| 権限昇格 | スコープチェックの欠如 | 各エンドポイントでのrequiredScopes検証 |
この脅威モデルを理解することで、特定のデプロイメントに追加のアプリケーションレベルのセキュリティ対策が必要かどうかを評価できます。
次のステップ
- トークン検証 — 詳細な検証APIとクレーム処理
- DPoP検証 — リプレイ保護を備えた送信者制約トークンの検証
- JWKS管理 — 鍵のキャッシュ、ローテーション、カスタムプロバイダー
- イントロスペクションと失効 — 認可サーバーでのトークンの問い合わせと失効