mcpField
A
attacking-graphql

attacking-graphql

github.com/trilwuセキュリティ17749 回インストール12 週間前に更新
リポジトリ

attacking-graphql について

API セキュリティ担当者向けの GraphQL テストスキルです。許可された範囲でのスキーマ復元とリゾルバー単位の認可確認を案内し、バッチ処理、インジェクション、クエリ形状のリスクを扱います。

主な機能

  1. 01イントロスペクションなどによるスキーマ復元
  2. 02フィールド・リゾルバー単位の認可テスト
  3. 03エイリアスとバッチ処理の悪用確認
  4. 04クエリ深度と複雑度のリスク評価
  5. 05インジェクション、CSRF、サブスクリプション認可のテスト

活用シーン

  1. 01スキーマが不明な GraphQL API の把握
  2. 02ユーザーやテナントをまたぐフィールドアクセスの確認
  3. 03ログイン操作のエイリアス・バッチ制限の評価
  4. 04制限したクエリによる DoS リスクの検証

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/trilwu/secskills --skill attacking-graphql

インストール後、Claude Code で/attacking-graphqlを入力して読み込まれたか確認します。

詳細

GraphQL への攻撃

GraphQL では認可の対象がルートからリゾルバーへ移りますが、ほとんどのチームはアクセス制御をそれに合わせて移行していません。/admin/* を保護するという REST の慣習は、すべての操作が単一のエンドポイントに届く場合には通用しません。そのため、繰り返し見つかるのは、珍しい GraphQL のバグではなく、誰も保護する必要があると考えなかったフィールドにおける一般的な認可の不備です。

使用する場面

  • 対象が /graphql、/graphiql、/v1/graphql、/api/graphql を公開している
  • リクエスト本文に query、mutation、subscription、operationName が含まれている
  • レスポンスが {"data": ..., "errors": [...]} 形式になっている
  • モバイルアプリまたは SPA クライアントがバックエンドに GraphQL を送信している
  • スキーマが手元にない API の構造を把握する必要がある

使用しない場面

  • REST または gRPC — testing-apis または attacking-grpc-protobuf を使用してください
  • ソースコードがある — auditing-code-for-vulnerabilities を使用してください。リゾルバーを読むほうが、より速く、より完全に把握できます
  • 周辺のウェブアプリ — testing-web-applications を使用してください
  • 本番環境に対するサービス妨害の構築 — リスクをモデル化し、最小限の方法で立証し、負荷をかける前に明示的な許可を得てください

スキーマを復元する

# Standard introspection
curl -s https://target/graphql -H 'Content-Type: application/json' \
  -d '{"query":"query{__schema{types{name fields{name args{name type{name}}}}}}"}' | jq .

# Tooling that renders it usefully
graphql-cop -t https://target/graphql
clairvoyance https://target/graphql -o schema.json     # works WITHOUT introspection
graphw00f -t https://target/graphql                    # fingerprint the engine

イントロスペクションが無効になっていても、通常はスキーマを復元できます。
多くのエンジンは、フィールド名を間違えると「もしかしてこれでは?」という候補を返すため、有効な名前が文字クラスごとに漏えいします。clairvoyance はこれを自動化します。

# Field suggestion leak — the response names fields you did not know
curl -s https://target/graphql -d '{"query":"{ userr { id } }"}' -H 'Content-Type: application/json'
# → "Cannot query field \"userr\" on type \"Query\". Did you mean \"user\"?"

その他のスキーマ入手先として、クライアントバンドル(クエリは通常 JavaScript にインライン化されています)、誤って配信されている .graphql ファイル、永続クエリのマニフェスト、公開状態で放置されている Apollo Studio などのツールがあります。

エンジンを特定する — 挙動には大きな違いがあります。Apollo、graphql-js、Hasura、graphene、gqlgen、HotChocolate はそれぞれ、バッチ処理、候補提示、深さ制限、エラーの詳細度について異なるデフォルト設定を持っています。graphw00f を使うとエンジンを特定できます。

認可が最重要

エンドポイントごとではなく、フィールドおよびリゾルバーごとに認可をテストしてください。user(id:) は保護されているのに、user { organization { members { email } } } は保護されていないスキーマは、典型的な発見事項です。

# 1. Object-level: request another tenant's or user's object by ID
query { user(id: "other-user-id") { id email phone } }

# 2. Field-level: the object is yours, but a field should not be exposed
query { me { id email passwordHash internalNotes stripeCustomerId } }

# 3. Traversal: reach a protected object through an unguarded edge
query { post(id: 1) { author { email resetToken orders { total } } } }

# 4. Mutations: the usual suspects, called directly
mutation { updateUser(id: "other", input: {role: ADMIN}) { id role } }
mutation { deleteAccount(id: "other") { success } }

リレーションをたどるテストが最も有効です。 開発者は、意識しているエントリーポイントを保護します。しかし、ネストされたエッジは親リゾルバーで許可されたアクセスをそのまま引き継ぎます。そして多くの場合、親リゾルバーでは何も制限されていません。スキーマのエッジを列挙し、正当に所有するオブジェクトから、所有していないオブジェクトまでたどってください。

匿名ユーザー、低権限ユーザー、別テナントのユーザーという、利用可能な各権限レベルで、すべてのテストを繰り返してください。

バッチ処理とエイリアスの悪用

1 つの HTTP リクエストに複数の操作を含められるため、リクエスト単位のレート制限を回避できます。これは、2FA やパスワードに対するブルートフォース攻撃でよく見られる回避手法です。

# Aliases: N attempts, one request
{
  a1: login(user:"admin", pass:"1234") { token }
  a2: login(user:"admin", pass:"1235") { token }
  a3: login(user:"admin", pass:"1236") { token }
}
[ {"query":"{ user(id:1){email} }"},
  {"query":"{ user(id:2){email} }"},
  {"query":"{ user(id:3){email} }"} ]

複数のエンジンでは、配列バッチ処理がデフォルトでサポートされています。どちらの形式もテストしてください。制限のかかり方が異なることが多く、HTTP 層で適用されるレート制限からは、いずれも 1 つのリクエストに見えます。

クエリ構造を利用したサービス妨害

# Depth: cyclic relationships nested repeatedly
{ user { posts { author { posts { author { posts { id } } } } } } }

# Breadth: aliases multiply one expensive resolver
{ a: search(q:"x"){id} b: search(q:"x"){id} c: search(q:"x"){id} ... }

# Field duplication amplifies without depth
{ user { id id id id id id ... } }

深さ制限、複雑度/コスト制限、タイムアウト、ページネーションのノード数制限、エンジンが N+1 のリゾルバー呼び出しをまとめて処理するか、ノードごとにクエリを発行するかを確認してください。小さく制限したクエリでリスクを立証してください。実際にサービスを枯渇させるのではなく、深さごとの応答時間の増加を測定します。3 階層のクエリが 30 ミリ秒で応答するのに、8 階層のクエリに 30 秒かかるなら、それが証拠になります。API を停止させる必要はありません。

リゾルバーを介したインジェクション

リゾルバーは他のハンドラーと同様にデータベースにアクセスするため、一般的な攻撃手法が適用されます。ただし、引数は強く型付けされているため、それをバリデーションと勘違いする人がいる点には注意が必要です。

{ user(filter: "1' OR '1'='1") { id } }                 # SQL/NoSQL injection
{ users(where: {email: {_ilike: "%"}}) { email } }       # Hasura-style filter abuse
{ file(path: "../../etc/passwd") { contents } }          # traversal

Hasura などの自動生成 API には特に注意してください。クライアントに豊富な where フィルターを直接公開するため、行レベルセキュリティの設定が緩いと、フィルター言語そのものが脆弱性になります。

実施する価値のあるその他のチェック

  • GET またはフォーム形式のクエリによる CSRF。 エンドポイントが ?query=mutation{...} または application/x-www-form-urlencoded を受け付ける場合、プリフライトなしでクロスオリジンからミューテーションにアクセスできます。
  • 本番環境でのイントロスペクション — これ単独では重大度は低いものの、スキーマの全容を渡すことになります。
  • スタックトレース、リゾルバー名、内部パスが漏えいする詳細なエラー。
  • WebSocket 経由のサブスクリプション — 接続時にのみ認可がチェックされ、その後は再チェックされないことがよくあるため、ログアウトや権限変更後も長時間接続するサブスクリプションが維持されます。
  • 永続クエリ — クライアントがハッシュのみを送信する場合でも、サーバーが任意のクエリを受け付けるか確認してください。任意のクエリにフォールバックする APQ 実装には、保護効果がありません。
  • multipart 仕様によるファイルアップロード。これは独立したパーサーです。

退けるべき合理化

  • 「イントロスペクションは無効なので、スキーマは保護されています。」 提案機能から漏れるうえ、クライアントバンドルにも含まれています。
  • 「ゲートウェイがエンドポイントをレート制限しています。」 カウントされるのは HTTP リクエストです。エイリアスとバッチ処理を使えば、1 つのリクエストに千件の操作を含められます。
  • 「引数には型があるので、インジェクションは不可能です。」 型が制約するのは形式であり、内容ではありません。String もデータベースに渡されます。
  • 「そのフィールドは管理 UI でしか使われません。」 スキーマはすべての人に公開されています。UI はアクセス制御ではありません。
  • 「最上位のクエリで認可をチェックしています。」 ネストされたエッジをテストしてください。問題はそこにあります。
  • 「深さ制限は導入済みです。」 幅とエイリアスも確認してください。
  • 「サービスを停止させて DoS を証明します。」 段階的な影響を示してください。負荷をかける前に許可を得てください。

ATT&CK の対象範囲

secskills-core/ttp-index.json から生成されます — このファイルを編集してから、 python3 scripts/sync_attack.py --write を実行してください。レポートで引用する前に、 ID が現在の ATT&CK リリースと一致していることを再確認してください。

初期アクセス (TA0001)

  • T1190 公開アプリケーションの悪用 — testing-web-applications、testing-apis、enumerating-network-services、attacking-grpc-protobuf、exploiting-deserialization、exploiting-ssrf、exploiting-xxe も参照してください

これらのいずれかに関する検出コンテンツ:engineering-detections。プロアクティブな検索:hunting-threats。侵害後の対応:responding-to-incidents。

参考資料

  • testing-apis — 本項目が特化する、一般的な API テスト手法
  • auditing-code-for-vulnerabilities — ソースがある場合のリゾルバーレベルのレビュー
  • testing-web-applications — エンドポイントを取り巻くアプリケーション
  • reporting-security-findings — 認可に関する指摘の重大度
  • graphw00f、clairvoyance、graphql-cop、InQL(Burp)、Altair/GraphiQL

コメント