OAuth 2.0アーキテクチャ:委任認可と実装パターン

ードパーティアプリケーション連携において、ユーザーのクレデンシャル(ID/パスワード)を直接共有することは、セキュリティ上の致命的なアンチパターンです。かつてのリバースエンジニアリングやスクレイピングに依存した連携手法は、クレデンシャルの漏洩リスクとアクセス制御の粒度の粗さという二重の課題を抱えていました。これらの問題を解決し、リソースへのアクセス権限を安全に「委任(Delegate)」するための業界標準プロトコルがOAuth 2.0です。

1. 委任認可の構造とアクター定義

OAuth 2.0は単なるログイン機能ではなく、リソースオーナーがクライアントアプリケーションに対し、自身のクレデンシャルを明かすことなく、限定された権限を与えるためのフレームワークです。RFC 6749で定義されている4つの主要アクターを理解することは、システム設計の基礎となります。

Architecture Note: クライアント(Client)は、ブラウザ上で動作するSPAやモバイルアプリ(Public Client)と、サーバーサイドでクレデンシャルを保持できるアプリ(Confidential Client)に分類され、この分類が採用すべきグラントタイプを決定します。
  • Resource Owner (User): データ(リソース)の所有権を持ち、アクセスを許可する主体。
  • Client (Application): リソースオーナーの代理としてリソースにアクセスするアプリケーション。
  • Authorization Server: ユーザー認証を行い、権限委譲の同意(Consent)を取り付け、トークンを発行するサーバー。
  • Resource Server (API): アクセストークンを検証し、リソースを提供するAPIサーバー。

2. トークンエコノミーとスコープ制御

OAuth 2.0のセキュリティモデルは、有効期限の短い「アクセストークン」と、それを更新するための「リフレッシュトークン」という2種類のトークンによって成立しています。

アクセストークン (Access Token)

リソースサーバーへの物理的なアクセスキーです。BearerトークンとしてHTTPヘッダーに付与されるのが一般的です。ステートレスな検証を可能にするため、JWT (JSON Web Token) 形式が採用されるケースが増えていますが、トークンサイズが大きくなるトレードオフが存在します。

GET /api/v1/user/profile HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

リフレッシュトークン (Refresh Token)

アクセストークンの有効期限(通常1時間程度)が切れた際、ユーザーの再認証なしに新しいアクセストークンを取得するための永続的なトークンです。このトークンは認可サーバーに対してのみ使用され、リソースサーバーには送信されません。

Security Warning: リフレッシュトークンは長期間有効であるため、漏洩時のリスクが極めて高いです。データベースへの保存時は必ずハッシュ化または暗号化を行い、クライアント側での保管場所(HttpOnly Cookie vs LocalStorage)も慎重に検討する必要があります。

3. グラントタイプの選択と実装戦略

OAuth 2.0では複数の「グラントタイプ(認可フロー)」が定義されていますが、現代のWeb/モバイルアプリケーション開発において推奨されるフローは限定されています。

Authorization Code Grant + PKCE

かつてSPA向けに使用されていたImplicit Grantは、アクセストークンがURLフラグメントに露出するため、現在は非推奨(Deprecated)です。代わりに、セキュリティ強度を高めた「PKCE (Proof Key for Code Exchange) 付き認可コードグラント」が、パブリッククライアント・コンフィデンシャルクライアント問わず標準となっています。

PKCEは、認可リクエスト時に生成したハッシュ値(Code Challenge)と、トークン交換時に送信する元の値(Code Verifier)を照合することで、認可コードの横取り攻撃(Authorization Code Interception Attack)を防ぎます。

# PKCEフローにおける検証ロジックの概念コード
import hashlib
import base64

def generate_pkce_pair():
    # 1. 乱数によるCode Verifier生成
    verifier = secrets.token_urlsafe(32)
    
    # 2. SHA256でハッシュ化し、Base64URLエンコードしてCode Challenge生成
    digest = hashlib.sha256(verifier.encode('utf-8')).digest()
    challenge = base64.urlsafe_b64encode(digest).rstrip(b'=').decode('utf-8')
    
    return verifier, challenge

# クライアントは認可リクエスト時に `challenge` を送信
# トークンリクエスト時に `verifier` を送信
# 認可サーバー側で hash(verifier) == challenge を検証する

Client Credentials Grant

ユーザーが介在しないM2M(Machine-to-Machine)通信、例えばバックエンドバッチ処理や内部マイクロサービス間の通信で使用されます。ここではクライアント自体がリソースオーナーとみなされ、client_idclient_secret のみでトークンを取得します。

4. 認証と認可の分離:OIDC (OpenID Connect)

OAuth 2.0はあくまで「認可(Authorization)」のプロトコルであり、「認証(Authentication)」の手順やフォーマットは規定していません。これを補完するために策定されたのがOIDCです。

OIDCはOAuth 2.0の拡張仕様であり、アクセストークンと同時にIDトークンを発行します。IDトークンはユーザーの属性情報(Identity)を含むJWTであり、クライアントはこのトークンを検証することで「誰がアクセスしているか」を特定できます。

項目 OAuth 2.0 (Access Token) OIDC (ID Token)
目的 リソースへのアクセス許可(認可) ユーザーの本人確認(認証)
利用先 リソースサーバー(API) クライアントアプリ自体
形式 任意(多くの場合はJWTだがOpaqueも可) JWT (JSON Web Token) 必須

5. セキュリティ・ベストプラクティス

OAuth 2.0の実装において、仕様準拠だけでなく、攻撃ベクトルを考慮した防御策の実装が不可欠です。

StateパラメータによるCSRF対策

認可リクエスト時にランダムな文字列(State)を含め、コールバック時に返却されたStateが一致するか検証することで、CSRF(クロスサイトリクエストフォージェリ)攻撃を防ぎます。多くの最新ライブラリでは自動化されていますが、独自実装する場合は必須要件です。

Redirect URIの完全一致検証

認可サーバー側で、事前に登録されたRedirect URIとリクエスト時のURIが完全に一致するかを検証する必要があります。前方一致などの曖昧な検証を行うと、オープンリダイレクト脆弱性を突かれ、認可コードが攻撃者のサーバーへ送信されるリスクがあります。

Best Practice: スコープは「最小権限の原則」に従い、必要最低限のものだけを要求してください。過剰な権限要求は、万が一のトークン漏洩時の被害範囲を拡大させるだけでなく、ユーザーの不信感を招く原因となります。

結論:トレードオフと今後の展望

OAuth 2.0は、実装の複雑さと引き換えに、堅牢なセキュリティと柔軟な連携基盤を提供します。特にマイクロサービスアーキテクチャにおいては、APIゲートウェイ層でのトークン検証やスコープ制御が中心的な役割を果たします。エンジニアは、単にライブラリを使用するだけでなく、背後にあるトークンのライフサイクルや攻撃ベクトルを理解し、ユースケースに応じた適切なグラントタイプとストレージ戦略を選択する責任があります。

Post a Comment