当社のAIエンジンNeutronが、UCバークレーのCyberGymベンチマークで96.75%のスコアを記録しました。 詳細を見る

セキュリティ

セキュリティ

AIペンテストエンジンがGraphQLサブスクリプションにおけるクリティカルなWebSocket BFLAを発見

OstorlabのAIペンテストエンジンは、GraphQL WebSocketエンドポイントにおいて、リアルタイム翻訳サービスへの認証なしのアクセスを許す、クリティカルな関数レベルの認可の不備(BFLA)脆弱性を体系的に発見しました。本ケーススタディでは、発見から概念実証に至るまで、AIの段階的なプロセスを詳述します。

はじめに

現代のWebアプリケーションは、動的なユーザー体験を提供するために、WebSocketのようなリアルタイム通信プロトコルにますます依存しています。GraphQLと組み合わせると、この組み合わせはサブスクリプションを通じて強力かつ効率的なデータ交換をもたらします。しかし、この複雑さは、特に認可をめぐって、新たなアタックサーフェスも生み出します。GraphQLサブスクリプションを保護するには、接続、サブスクリプションの開始、イベントごとのデータフィルタリングという複数の段階で厳格なチェックが必要です。

OstorlabのAIペンテストエンジンは、この複雑さを乗りこなすように設計されています。手動テストでは見逃しかねない認可のギャップを、体系的に特定し検証します。本記事では、AIエンジンがGraphQL WebSocketエンドポイントにおいて、認証なしのあらゆるユーザーがサブスクリプションを実行して機微なデータを受け取れる、クリティカルな関数レベルの認可の不備(BFLA)脆弱性をどのように発見したかを紹介します。

GraphQL WebSocket BFLAに対するOstorlab AIペンテスト

エンジンには、高レベルの目標が与えられました。対象アプリケーションREDACTEDでWebSocketサブスクリプションの認可を検証する、というものです。その後に続いたのは、発見、探査、確認という、手順を踏んだ段階的なプロセスでした。

ステップ1:認証なしのGraphQL WebSocket接続のテスト

AIの最初のアクションは、最も基本的な挙動をテストすることでした。WebSocketエンドポイントは、認証情報なしで接続を受け入れるのか、という点です。

/subscriptionsへの、認証なしのWSハンドシェイクのベースライン。目標:GraphQL WebSocketサーバーが認証情報なしで接続を受け入れるかどうかを検証する。

実行概要: エンジンは、graphql-transport-wsサブプロトコルを使ってwss://REDACTED/subscriptionsに接続しました。空のペイロードを持つ標準的なconnection_initフレームを送信し、Authorizationトークンは一切提供しませんでした。

結果: サーバーは{"type":"connection_ack"}で応答しました。実際には、ackフレームを2つ送信しており、これはAIが指摘した異常です。

結論: 認証なしのハンドシェイクが受け入れられました。サーバーは接続フェーズで認証を要求していません。これは、潜在的な弱点を示す最初のシグナルです。

ステップ2:認証なしでのGraphQLサブスクリプションスキーマのイントロスペクション

接続が確立されたことで、AIの次の論理的なステップは、どの操作が利用可能かを突き止めることでした。認証なしのWebSocket接続を通じて、GraphQLのイントロスペクションクエリの実行を試みました。

WebSocket経由で認証なしのGraphQLイントロスペクションを一度試み、サブスクリプション操作を列挙するとともに、認証なしの操作が許可されているかどうかを検証する。

実行概要: エンジンは、Subscription型に焦点を当てたイントロスペクションクエリを含むsubscribeフレームを送信しました。 {"id":"1","type":"subscribe","payload":{"query":"query { __schema { subscriptionType { name fields { name } } } }","variables":{}}}

結果: サーバーはスキーマデータを含むnextフレームを返し、続いてcompleteフレームを返しました。

{
  "id": "1",
  "type": "next",
  "payload": {
    "data": {
      "__schema": {
        "subscriptionType": {
          "name": "Subscription",
          "fields": [
            { "name": "generateSamplePromptResult" },
            { "name": "translateContent" },
            { "name": "upsertRewardSubscription" }
          ]
        }
      }
    }
  }
}

結論: サーバーはWebSocket経由での認証なしのイントロスペクションを許可しており、これは重大な情報漏えいです。これにより3つのサブスクリプション操作が明らかになり、エンジンに認可テストの明確な標的を与えました。

ステップ3:translateContentサブスクリプションのBFLAテスト

エンジンは、最初の直接テストの対象としてtranslateContentを選びました。その入力スキーマをイントロスペクションした後、構文的に有効だが無害なサブスクリプションリクエストを作成しました。

最小限の有効な入力を用いて、translateContentへの認証なしのサブスクリプションを一度試み、認証なしのサブスクリプションが処理されるかどうかを検証する。

実行概要: エンジンはtranslateContentに対するsubscribeフレームを送信し、最小限のペイロードを提供し、実行を確認するために__typenameのみを要求しました。 {"id":"1","type":"subscribe","payload":{"query":"subscription($data: ContentTranslationSubscriptionInput!){ translateContent(data:$data){ __typename } }","variables":{"data":{"targetLanguage":"en","sourceContent":"hello"}}}}

結果: サーバーはサブスクリプションを受け入れ、2つのnextフレームを返し、続いてcompleteを返しました。

{"id":"1","type":"next","payload":{"data":{"translateContent":{"__typename":"TranslateContentResponse"}}}}
{"id":"1","type":"next","payload":{"data":{"translateContent":{"__typename":"TranslateContentResponse"}}}}
{"id":"1","type":"complete"}

結論: translateContentサブスクリプションは、認証なしで正常に処理されました。これは情報漏えいの域を超え、バックエンド関数の能動的かつ認証なしの実行に当たります。

ステップ4:GraphQL WebSocket BFLAを介したデータ露出の検証

__typenameを受け取ることは操作が実行されたことを証明しますが、実際のデータを返すのでしょうか。AIの最後のステップは、データ露出を確認するために具体的なフィールドを要求することでした。

translateContentから実際のフィールドを要求することで、認証なしでのデータ返却を確認する。

実行概要: エンジンはサブスクリプションを繰り返し、今回はcontent、isFinal、subscriptionIdの各フィールドを要求しました。 {"id":"1","type":"subscribe","payload":{"query":"subscription($data: ContentTranslationSubscription-Input!){ translateContent(data:$data){ content isFinal subscriptionId } }","variables":{"data":{"targetLanguage":"en","sourceContent":"hello"}}}}

結果: サーバーは、翻訳されたテキストを含むnextフレームを返しました。

{
  "id": "1",
  "type": "next",
  "payload": {
    "data": {
      "translateContent": {
        "content": "Hello",
        "isFinal": false,
        "subscriptionId": "yrpsrqkv7li34w23r25dx4b2"
      }
    }
  }
}
{
  "id": "1",
  "type": "next",
  "payload": {
    "data": {
      "translateContent": {
        "content": "Hello",
        "isFinal": true,
        "subscriptionId": "yrpsrqkv7li34w23r25dx4b2"
      }
    }
  }
}

結論: これは、関数レベルの認可の不備(BFLA)脆弱性の決定的な証拠です。認証なしの攻撃者がtranslateContent関数を実行し、その結果を受け取ることができます。

ステップ5:GraphQLサブスクリプションにおける一貫性のない認可の特定

この問題の範囲を把握するため、エンジンは発見したもう一つのサブスクリプションgenerateSamplePromptResultもテストしました。

generateSamplePromptResultへの認証なしのサブスクリプションを一度試み、操作ごとの認可を評価する。

実行概要: エンジンは、generateSamplePromptResultに対する有効な、認証なしのサブスクリプションリクエストを送信しました。

結果: サーバーはただちにコード4500、理由"You are not authorized to perform this action"で接続を閉じました。

結論: この操作については認可が正しく適用されています。この検出結果は極めて重要です。なぜなら、それは一貫性のないセキュリティ制御を示しているからです。これは、開発者が一部のエンドポイントを保護しながら他のエンドポイントを見落とす、よくあるアンチパターンです。

最終レポート:GraphQL WebSocket BFLAに関するOstorlabの検出結果

1. エグゼクティブサマリー

OstorlabのAIペンテストエンジンは、wss://REDACTED/subscriptionsにあるGraphQL WebSocketエンドポイントにおいて、クリティカルな関数レベルの認可の不備(BFLA)脆弱性を発見しました。translateContentサブスクリプション操作が公開された状態になっており、認証なしのあらゆるユーザーがそれを実行してストリーミング結果を受け取ることができました。これは直接的な権限バイパスに当たり、リソースの悪用や潜在的なデータ露出につながる可能性があります。エンジンはまた、同じエンドポイント上の他のサブスクリプションがアクセス制御を正しく適用していたことから、一貫性のない認可にも気づきました。

2. 手法

AIエンジンは、WebSocketの認可をテストするための体系的な手法に従いました。 1. エンドポイントの発見:GraphQL WebSocketエンドポイントとそのプロトコル(graphql-transport-ws)を特定しました。 2. 認証なしのベースライン:サーバーが認証なしのconnection_initハンドシェイクを受け入れることを確認しました。 3. スキーマの列挙:認証なしのGraphQLイントロスペクションを活用し、利用可能なサブスクリプション操作を発見しました。 4. 操作ごとのテスト:発見したサブスクリプション(translateContent、generateSamplePromptResult)を順に試し、認証なしでの実行を試みました。 5. エビデンスの確認:ステータスメッセージだけでなく、具体的なデータフィールドを要求して受け取ることで、データ漏えいを確認しました。

3. 検出結果

  • translateContentサブスクリプションにおけるBFLA:主要な検出結果は、translateContentサブスクリプションに認可チェックが一切欠けていることです。攻撃者は、認証なしで接続、サブスクライブし、翻訳結果を受け取ることができます。これは、有効なペイロードを送信し、応答として翻訳されたテキストを受け取ることで確認されました。この脆弱性は、明示的に無効なトークンであっても無視されることを示すことで、さらに裏付けられました。
  • 認証なしのイントロスペクションを介した情報漏えい:サブスクリプション向けのGraphQLスキーマがWebSocket経由で公開されており、攻撃者が利用可能な操作とそれらが必要とする入力を容易に発見できてしまいます。
  • 一貫性のない認可制御:translateContentは脆弱でしたが、同じエンドポイント上のgenerateSamplePromptResultサブスクリプションは、認証なしのリクエストを正しく拒否しました。この不整合は、脆弱なリゾルバーにアノテーションまたはセキュリティ制御が欠けていることを示唆しています。

4. 修復

これらの検出結果に対処するため、次のことを推奨します。 1. デフォルトで認証を適用する:デフォルト拒否のポリシーを実装してください。すべてのGraphQLリゾルバー、特にサブスクリプション向けのものは、明示的に公開とマークされていない限り、有効な認証済みセッションを必須とすべきです。 2. 認可ロジックを一元化する:ミドルウェアまたはフレームワークレベルのフックを使い、接続時とあらゆるサブスクリプションリクエスト時の両方で、すべてのWebSocket操作にわたって認証と認可のチェックを一貫して適用してください。 3. 本番環境ではイントロスペクションを無効化する:GraphQLのイントロスペクションは開発には役立ちますが、攻撃者がAPIの対象領域を容易にマッピングするのを防ぐため、公開されているエンドポイントでは無効化すべきです。

5. まとめ

現代のリアルタイムAPIを保護するには、認可が一貫して適用される多層防御のアプローチが必要です。本ケーススタディは、OstorlabのAIペンテストエンジンが、複雑なWebSocket実装において、BFLAや一貫性のないセキュリティ制御のような、見落とされやすいながらもクリティカルな欠陥を体系的に発見できることを示しています。発見から悪用に至るまで、攻撃者の論理的な進め方を模倣することで、エンジンは決定的で実行可能なエビデンスを提供し、開発者がより安全なアプリケーションを構築するのを支援します。

タグ:

security, ai, poc, pentest, graphql