【実務・中級編】 OAuth 2.0のスコープ昇格攻撃 – オフェンシブセキュリティ & リバースエンジニアリング防御ガイド

OAuth 2.0の「スコープ昇格」:認可サーバーの甘いマスクを剥がす攻撃手法と対策

現場でセキュリティ診断をしていると、OAuth 2.0の実装で「クライアントの言いなり」になっている認可サーバーによく出くわします。開発者は「クライアントが要求したスコープをそのまま許可すれば楽だし、動くからいいだろう」と考えがちですが、それが命取りです。

今回は、OAuth 2.0のスコープ昇格攻撃(Scope Escalation)に焦点を当て、なぜこれが脆弱性になり得るのか、そしてどう防御すべきかを、現場の視点から紐解いていきます。

—

1. なぜ「スコープ昇格」が起きるのか

OAuth 2.0において、scopeパラメーターは「このアクセストークンで何ができるか」を定義する境界線です。攻撃者は、本来許可されていない権限(例: readしか持っていないはずがwriteやadminを要求)を認可リクエストに紛れ込ませます。

認可サーバーがこのscopeを適切に検証せず、「クライアントが言っているから」という理由だけでそのまま発行してしまうと、権限昇格の完成です。これは、IDカードの発行窓口で「私は社長です」と名乗っただけで、社長用の通行証が発行されてしまうのと同じくらい危険な状態です。

攻撃シナリオ (PoC)

攻撃者は、正規の認可リクエストをインターセプト(または不正なクライアントを作成)し、パラメーターを操作します。

GET /authorize?response_type=code&client_id=client_123&redirect_uri=https://app.com/callback&scope=read_profile+delete_all_users HTTP/1.1
Host: auth.example.com

もし、認可サーバーが delete_all_users というスコープを「誰に発行してよいか」を管理していない場合、ユーザーが「ログイン」ボタンを押した瞬間に、管理者権限を持ったトークンが攻撃者の手に渡ります。

—

2. 現場で使える防御戦略:検証の「ホワイトリスト化」

この攻撃を防ぐための鉄則は、「認可サーバーがスコープの決定権を握る」ことです。クライアントからの要求はあくまで「希望」であり、最終的な許可はサーバー側が保持するホワイトリストに基づいて行わなければなりません。

サーバーサイドでの検証ロジック(Python/Flask風)

認可サーバーのロジックには、以下のように「クライアントIDごとの許可リスト」を実装してください。

# 認可サーバーの検証ロジック例
def validate_scopes(client_id, requested_scopes):
    # クライアントごとに許可されているスコープのホワイトリスト
    # 本来はデータベースで管理すべきです
    whitelist = {
        "client_123": ["read_profile", "read_email"],
        "client_admin": ["read_profile", "write_profile", "delete_all_users"]
    }
    
    allowed = whitelist.get(client_id, [])
    
    # 要求されたスコープがホワイトリストに含まれているかチェック
    validated_scopes = [s for s in requested_scopes if s in allowed]
    
    # もし要求と結果が異なる(不正な要求があった)場合、ログを吐いて警告する
    if set(requested_scopes) != set(validated_scopes):
        logger.warning(f"不正なスコープ要求を検知: Client={client_id}, Requested={requested_scopes}")
        
    return validated_scopes

—

3. インフラレイヤーでの多層防御(Nginxの設定)

アプリケーションコードの修正は必須ですが、そもそも不正なリクエストをエッジで弾く姿勢も重要です。WAFやNginxで「異常に長いスコープ文字列」や「意図しないスコープ名」をフィルタリングすることで、攻撃の難易度を劇的に上げることができます。

# Nginxで不正なスコープを含むリクエストを拒否する例
location /authorize {
    # scopeパラメーターに危険な文字列が含まれていないか簡易的にチェック
    if ($arg_scope ~* "(delete|drop|admin|root)") {
        return 403 "Invalid scope requested.";
    }
    proxy_pass http://auth_backend;
}

—

4. エンジニアへのアドバイス:インシデントを防ぐために

多くのエンジニアは「機能の実装」に追われ、OAuth 2.0を「単なるログイン手段」として見てしまいがちです。しかし、アクセストークンは、Webアプリにおける「鍵束」そのものです。

私が現場でよく見る「やってはいけない実装」をまとめました。これらを見つけたら即座に修正すべきです。

1. スコープの動的生成: クライアントが投げた文字列をそのままDBに保存したり、JWTに埋め込んだりしていませんか? 常に静的なホワイトリストとの照合を行ってください。
2. ログ出力の欠如: 不正なスコープ要求は、攻撃の予兆(Reconnaissance)です。必ずセキュリティログに記録し、SIEM等でアラートを飛ばすようにしてください。
3. トークン発行後の検証不足: リソースサーバー側でも、受け取ったアクセストークンのスコープが「そのリソースへのアクセスに十分か」を必ず再検証してください。認可サーバーだけを信じてはいけません。

最後に

セキュリティとは、決して「完璧な壁」を作ることではなく、「どこまで深く疑い、泥臭く検証し続けられるか」という姿勢そのものです。OAuth 2.0のスコープ検証は、その最も基本的な、しかし最も疎かにされがちな防波堤です。

皆さんの実装している認証認可基盤が、今日からより堅牢になることを願っています。もし「自分のコードが不安だ」と思ったら、まずはホワイトリストの再確認から始めてみてください。それが、最強の防御の第一歩です。

コメント

タイトルとURLをコピーしました