Webアプリケーションにおける OAuth セキュリティのベストプラクティス

OAuth 2.0 は API 認証の標準となっていますが、安全な実装は依然として困難です。トークンの盗難、非推奨フロー、ブラウザ固有の脆弱性が日々 Web アプリケーションを脅かす中、開発者には実際に機能する明確なガイダンスが必要です。本記事では、RFC 9700 の要件から SPA 固有のパターンまで、2025年に必要な OAuth 2.0 セキュリティのベストプラクティスを解説します。
重要なポイント
- OAuth トークンを localStorage や sessionStorage に保存しない—SPA ではメモリストレージを使用する
- OAuth 2.1 では、すべてのクライアントに対して PKCE を使用した Authorization Code Flow が必須
- リフレッシュトークンのローテーションと短命なアクセストークン(15〜30分)を実装する
- トークンをブラウザから完全に排除するために Backend for Frontend (BFF) パターンを検討する
OAuth セキュリティがこれまで以上に重要な理由
OAuth は、アプリケーション間でのパスワード共有を排除することで、ユーザーのデータを保護します。サードパーティアプリに認証情報を渡す(家の鍵を渡すようなもの)代わりに、OAuth は一時的でスコープが限定されたトークンを発行します—車のエンジンをかけることしかできないバレーキーを渡すようなものです。
しかし、問題があります:トークンは価値のある攻撃対象です。盗まれたアクセストークンは、即座に API へのアクセスを許可します。localStorage へのトークン保存、非推奨フローの使用、PKCE のスキップといった不適切な実装選択は、小さな脆弱性を重大な侵害に変えてしまいます。
主要なセキュリティリスクとその対策
トークンの盗難と保存
やってはいけないこと: トークンを localStorage や sessionStorage に保存すること。XSS 攻撃によってこれらの値が読み取られ、攻撃者が制御するサーバーに流出する可能性があります。
やるべきこと: SPA の場合、トークンはメモリ内にのみ保持します。サーバーサイドアプリの場合、暗号化されたサーバーサイドセッションに保存します。盗難による被害を限定するため、短命なアクセストークン(15〜30分)を使用します。
避けるべき非推奨フロー
Implicit Flow は廃止されました。RFC 9700 と OAuth 2.1 の両方がこれを禁止しています。なぜでしょうか? URL フラグメントでトークンを直接送信するため、ブラウザ履歴、リファラーヘッダー、ページ上の JavaScript に露出してしまうからです。
Resource Owner Password Credentials フローも避けるべきです—これはアプリがユーザーのパスワードを直接処理することを要求し、OAuth の本来の目的を損ないます。
常に使用すべき: SPA やモバイルアプリを含むすべてのクライアントに対して、PKCE を使用した Authorization Code Flow を使用します。
Discover how at OpenReplay.com.
最新の標準: RFC 9700 と OAuth 2.1
PKCE が必須に
PKCE (Proof Key for Code Exchange) は、認可コードの傍受攻撃を防ぎます。以下がその仕組みです:
// Generate code verifier and challenge
const verifier = generateRandomString(128);
const challenge = base64url(sha256(verifier));
// Include challenge in authorization request
const authUrl = `https://auth.example.com/authorize?` +
`client_id=app123&` +
`code_challenge=${challenge}&` +
`code_challenge_method=S256`;
// Send verifier when exchanging code for token
const tokenResponse = await fetch('/token', {
method: 'POST',
body: JSON.stringify({
code: authorizationCode,
code_verifier: verifier
})
});
OAuth 2.1 では、パブリッククライアントだけでなく、すべての認可コードフローに対して PKCE が必須となります。
mTLS または DPoP によるトークンバインディング
トークンバインディングは、盗まれたトークンが攻撃者によって使用されないことを保証します。主な2つのアプローチがあります:
- mTLS (Mutual TLS): トークンをクライアント証明書にバインドする
- DPoP (Demonstrating Proof of Possession): 証明書なしで暗号学的証明を使用する
どちらも、トークンを正当なクライアントに暗号学的に結びつけることで、トークンリプレイ攻撃を防ぎます。
リフレッシュトークンのローテーション
リフレッシュトークンを再利用しないでください。各リフレッシュで新しいリフレッシュトークンを発行し、古いものを無効化する必要があります。これにより、盗まれたリフレッシュトークンの悪用期間が制限されます:
// Server-side refresh token handling
async function refreshAccessToken(oldRefreshToken) {
// Validate and revoke old refresh token
await revokeToken(oldRefreshToken);
// Issue new token pair
return {
access_token: generateAccessToken(),
refresh_token: generateRefreshToken(), // New refresh token
expires_in: 1800
};
}
SPA の OAuth セキュリティ: 特別な考慮事項
シングルページアプリケーションは、すべてのコードがブラウザで実行されるため、独特の課題に直面します。ブラウザは敵対的な領域です—そこにあるデータは侵害される可能性があると想定してください。
Backend for Frontend (BFF) パターン
最も安全なアプローチは、トークンを完全にブラウザから排除することです。軽量なバックエンドプロキシが OAuth フローを処理し、サーバーサイドでトークンを維持し、SPA セッションには安全な httpOnly、sameSite クッキーを使用します。
Token Handler パターン
バックエンドの複雑さなしに SPA の利点を求めるチームには、Token Handler パターンが中間的な選択肢を提供します。これは以下を行う特殊なプロキシを使用します:
- OAuth フローを処理する
- トークンを安全に保存する
- SPA に短命なセッションクッキーを発行する
- クッキー認証されたリクエストをトークン認証された API 呼び出しに変換する
ブラウザにトークンを保存する必要がある場合
BFF が実現不可能な場合:
- トークンはメモリ内にのみ保存し、localStorage には決して保存しない
- Service Worker を使用してトークンアクセスを分離する
- 積極的なトークン有効期限(5〜15分)を実装する
- リフレッシュトークンをブラウザに保存しない
実装チェックリスト
✓ すべてのクライアントに対して PKCE を使用した Authorization Code Flow を使用する
✓ リダイレクト URI の完全一致を実装する
✓ トークンの有効期間を最小限の実用期間に設定する
✓ パブリッククライアントに対してリフレッシュトークンのローテーションを有効にする
✓ CSRF 保護のために state パラメータを使用する
✓ 高セキュリティアプリケーションにはトークンバインディング(mTLS/DPoP)を実装する
✓ SPA の場合: BFF または Token Handler パターンを検討する
✓ OAuth 2.1 準拠要件を監視する
まとめ
Web アプリケーションで OAuth を安全に実装するには、プロトコルの強みとアーキテクチャが直面する特定の脅威の両方を理解する必要があります。RFC 9700 の要件から始めましょう: 必須の PKCE、Implicit Flow の禁止、適切なトークン処理です。SPA の場合、BFF または Token Handler パターンを通じてトークンをブラウザから完全に排除することを真剣に検討してください。これらは単なるベストプラクティスではなく、2025年における OAuth セキュリティの最低基準です。
よくある質問
いいえ。機密データのないアプリでも、トークンに localStorage を使用すべきではありません。盗まれたトークンは、アカウント乗っ取り、API の悪用、より深刻な攻撃への足がかりとして使用される可能性があります。常にトークンはメモリに保存するか、サーバーサイドセッションを使用してください。
はい。OAuth 2.1 は、機密クライアントを含むすべての認可コードフローに対して PKCE を義務付けています。PKCE は、クライアントシークレットだけでは防げない認可コード傍受攻撃に対する多層防御を追加します。特に、侵害されたネットワークや悪意のあるブラウザ拡張機能が関与するシナリオで有効です。
Backend for Frontend パターンを使用して、バックエンドがリフレッシュトークンを管理し、安全なクッキーを介して SPA に短命なアクセストークンを提供します。または、アクセストークンの有効期限が切れたときに、ユーザーの操作なしで新しいトークンを取得するために prompt=none でサイレント認証を使用します。
Understand every bug
Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.