OAuth 2.0 / 2.1 を安全に実装・運用するための要点をまとめたドキュメント。 主な根拠は RFC 9700(OAuth 2.0 Security Best Current Practice) と OAuth 2.1 ドラフト(draft-ietf-oauth-v2-1)。

OAuth 2.1 はまだ IETF の Internet-Draft(RFC 未確定)だが、中身は OAuth 2.0 + PKCE + Security BCP の統合であり、主要な IdP はすでにその推奨に沿っている。新規実装は 2.1 準拠を前提にしてよい。


目次


1. フロー(グラントタイプ)の選択

ユースケース使うべきフロー備考
ユーザーが関与する Web アプリ / SPA / モバイル / デスクトップAuthorization Code + PKCE全クライアントで PKCE 必須
サーバー間通信(M2M)Client Credentialsユーザー文脈なし
入力制約デバイス(TV、CLI、IoT)Device Authorization Grant(RFC 8628)
トークン更新Refresh Tokenローテーション or 送信者制約を付ける

使ってはいけないフロー

  • Implicit Grant:アクセストークンが URL フラグメントに露出する。OAuth 2.1 で削除。
  • Resource Owner Password Credentials (ROPC):クライアントがパスワードを直接扱う。MFA・フィッシング対策と両立しない。OAuth 2.1 で削除。

2. PKCE(Proof Key for Code Exchange)

  • パブリック/コンフィデンシャルを問わず全クライアントで使う。 認可コード横取り・コードインジェクション対策になる。
  • code_challenge_method は S256 のみ。plain は使わない。
  • code_verifier は暗号論的に安全な乱数で 43〜128 文字。リクエストごとに新規生成し、使い終わったら破棄。
  • 認可サーバー側は、PKCE を伴わないリクエストを拒否する設定にする。

3. リダイレクト URI

  • 事前登録した URI との完全一致(exact string match)で検証する。 ワイルドカードや前方一致は使わない(オープンリダイレクト経由のコード漏えいの原因)。
    • 例外:ネイティブアプリのループバック(http://127.0.0.1:{port})はポート番号のみ可変を許容。
  • 本番は HTTPS 必須。
  • リダイレクト先ページにオープンリダイレクトを作らない。
  • 認可レスポンスを受けたページでは、外部リソース読み込みや Referer でコードが漏れないよう Referrer-Policy: no-referrer 等を設定する。

4. CSRF・ミックスアップ対策

  • state:推測不能な値をセッションに紐付けて送り、戻りで照合する。PKCE を使っていても、アプリ側の CSRF 対策として併用推奨。
  • nonce(OpenID Connect 利用時):ID トークンのリプレイ対策。
  • ミックスアップ攻撃対策:複数の認可サーバーと連携する場合、認可レスポンスの iss パラメータ(RFC 9207) を検証するか、認可サーバーごとに異なるリダイレクト URI を使う。

5. アクセストークン

  • 短命にする(目安:数分〜1時間)。
  • スコープは最小権限。 必要なものだけ要求し、リソースサーバーは毎回スコープを検証する。
  • オーディエンスを制限する(aud クレーム、または Resource Indicators(RFC 8707))。あるリソース向けトークンを別のリソースで使えないようにする。
  • URL のクエリパラメータでトークンを渡さない。 Authorization: Bearer ヘッダーを使う。
  • 送信者制約(Sender-Constrained Token) を可能なら導入する:
    • DPoP(RFC 9449):アプリケーション層で公開鍵に紐付け。SPA・モバイル向き。
    • mTLS(RFC 8705):クライアント証明書に紐付け。サーバー間向き。

JWT アクセストークンを検証する場合

  • 署名を必ず検証し、alg: none を拒否。許可するアルゴリズムを固定する。
  • iss / aud / exp / nbf / iat を検証する。
  • 鍵は JWKS から取得し、kid で選択、キャッシュとローテーションに対応する。
  • 即時失効が必要ならイントロスペクション(RFC 7662)を併用する。

6. リフレッシュトークン

  • パブリッククライアントでは以下のいずれかが必須:
    • リフレッシュトークンローテーション(使用のたびに新しいものを発行し、旧トークンは無効化)
    • 送信者制約(DPoP / mTLS)
  • ローテーション時、使用済みトークンの再利用を検知したら、そのトークンファミリー全体を失効させる(漏えいのシグナル)。
  • 絶対有効期限とアイドルタイムアウトを設定する。
  • ログアウト・パスワード変更・権限剥奪時に失効させる(Token Revocation(RFC 7009))。

7. クライアント認証(コンフィデンシャルクライアント)

  • client_secret よりも強い方式を優先:
    • private_key_jwt(非対称鍵による JWT クライアントアサーション)
    • mTLS(tls_client_auth / self_signed_tls_client_auth)
  • client_secret を使う場合は十分な長さの乱数、Secret Manager で管理、定期ローテーション。
  • シークレットをフロントエンド・モバイルアプリ・リポジトリに含めない。

8. クライアント種別ごとの指針

SPA(ブラウザアプリ)

  • 推奨:BFF(Backend for Frontend)パターン。 トークンはサーバー側で保持し、ブラウザには HttpOnly; Secure; SameSite Cookie のセッションだけを渡す。
  • BFF を使えない場合:
    • Authorization Code + PKCE
    • トークンはメモリ保持(localStorage は XSS で盗まれるため避ける)
    • リフレッシュトークンはローテーション必須、DPoP があればなお良い
  • 厳格な CSP で XSS リスクを下げる。

ネイティブアプリ(RFC 8252)

  • 認可は外部ブラウザ / システムブラウザ(ASWebAuthenticationSession、Custom Tabs)で行う。埋め込み WebView は使わない(資格情報を盗み見られる・SSO が効かない)。
  • リダイレクトは Claimed HTTPS URI(Universal Links / App Links) を優先。カスタム URL スキームは他アプリに乗っ取られうるため PKCE 必須。
  • トークンは Keychain / Keystore に保存。

サーバーサイド Web アプリ

  • コンフィデンシャルクライアントとして構成し、PKCE も併用。
  • トークンはサーバー側セッションに保存し、ブラウザに出さない。

9. 認可サーバー側の設定

  • 認可コードは一度だけ使用可能・短命(目安:〜60秒〜10分以内)。再利用を検知したら、そのコードで発行したトークンを失効。
  • 登録時にクライアントが使えるグラントタイプ・スコープ・リダイレクト URI を制限する。
  • PAR(Pushed Authorization Requests, RFC 9126) の利用を検討:認可リクエストのパラメータをバックチャネルで送り、改ざん・漏えいを防ぐ。
  • 高セキュリティ用途(金融・医療など)は FAPI 2.0 プロファイルに準拠する。
  • メタデータを公開する(RFC 8414 /.well-known/oauth-authorization-server または OIDC Discovery)。
  • 同意画面にはクライアント名・要求スコープを明示し、ユーザーが判断できるようにする。
  • 認可エンドポイントにクリックジャッキング対策(frame-ancestors / X-Frame-Options)を入れる。

10. 認証と認可を混同しない

  • OAuth は認可の仕組み。ログイン(認証)に使うなら OpenID Connect を使い、ID トークンを検証する。
  • アクセストークンを「ログインの証拠」として扱わない(別クライアント向けに発行されたトークンを持ち込まれる「トークン置換」攻撃の原因)。
  • ユーザー識別には sub(+iss)を使い、メールアドレスを一意キーにしない。

11. 運用・監視

  • 実績のある認証ライブラリ / IdP を使い、自前実装は最小限にする。
  • トークン発行・リフレッシュ・失効・失敗をログに残す(トークン本体はログに出さない)。
  • 異常検知:リフレッシュトークン再利用、短時間の大量発行、不審な IP・地域。
  • 署名鍵・クライアントシークレットのローテーション手順を用意する。
  • 依存ライブラリの脆弱性情報を追う。

チェックリスト

  • Implicit / ROPC を使っていない
  • 全クライアントで PKCE(S256)を使っている
  • リダイレクト URI を完全一致で検証している
  • state(OIDC なら nonce)を検証している
  • 複数 AS 連携時に iss を検証している
  • アクセストークンは短命・最小スコープ・オーディエンス制限あり
  • トークンを URL に載せていない
  • リフレッシュトークンはローテーション or 送信者制約あり、再利用検知あり
  • コンフィデンシャルクライアントは private_key_jwt / mTLS を検討した
  • SPA は BFF または同等の保護、トークンを localStorage に置いていない
  • ネイティブアプリは外部ブラウザを使い、WebView を使っていない
  • JWT 検証で alg・iss・aud・exp を確認している
  • ログインには OIDC の ID トークンを使っている
  • トークン・シークレットをログやリポジトリに出していない

参考仕様

仕様内容
RFC 6749OAuth 2.0 本体
RFC 6750Bearer Token の使い方
RFC 7636PKCE
RFC 8252ネイティブアプリ向け OAuth
RFC 8628Device Authorization Grant
RFC 7009Token Revocation
RFC 7662Token Introspection
RFC 8414認可サーバーメタデータ
RFC 8705mTLS クライアント認証・証明書バインドトークン
RFC 8707Resource Indicators
RFC 9068JWT アクセストークンのプロファイル
RFC 9126PAR
RFC 9207認可レスポンスの iss パラメータ
RFC 9449DPoP
RFC 9700OAuth 2.0 Security Best Current Practice
draft-ietf-oauth-v2-1OAuth 2.1
draft-ietf-oauth-browser-based-appsブラウザアプリ向けベストプラクティス
FAPI 2.0高セキュリティ用途向けプロファイル(OpenID Foundation)

0件のページ