すべての解説
深刻度:中APIのセキュリティ

公開されたGraphQLイントロスペクション:問題点と無効化の方法

GraphQLイントロスペクションは、APIの完全なスキーマ(すべての型、フィールド、引数、ミューテーション)を返す組み込みのクエリです。開発には欠かせませんが、非公開APIの本番環境ではアクセスできるべきではありません。有効なままだと、バックエンドの攻撃対象領域全体の地図になってしまいます。

これは何か

どのGraphQLサーバーも少数のメタフィールドを実装しており、代表的なものが__schemaと__typeです。`query { __schema { types { name fields { name } } } }` を/graphqlに1回POSTするだけで、完全な型定義が返されます。

GraphiQL、Apollo Sandbox、Playground UIなどのツールは、クエリの補完やスキーマエクスプローラーの表示にイントロスペクションを利用しています。開発ではそれが望ましい動作ですが、非公開APIの本番環境では、世界中の誰もが同じ地図をダウンロードできることを意味します。

なぜ重要か

スキーマにはモデル、リレーション、公開しているすべてのミューテーションの名前が含まれます。外部から発見されることを想定していなかった管理用、内部用、非推奨のフィールドも例外ではありません。攻撃者はこれを使って、認証バイパス、IDOR、インジェクションのバグを探るための操作リストを作成します。

すべてのフィールドが正しく認可されていても、refundOrder、impersonateUser、exportBackupのような隠しミューテーションの存在を知らせるべきではありません。イントロスペクションは、攻撃者の偵察フェーズを何時間もの推測から1回のリクエストに変えてしまいます。

直し方

Apollo Serverでイントロスペクションを無効にする

NODE_ENVがproductionのときはイントロスペクションをオフにします。ローカルではオンのままにして、チームがSandboxでスキーマを確認できるようにします。

import { ApolloServer } from "@apollo/server";

const server = new ApolloServer({
  typeDefs,
  resolvers,
  introspection: process.env.NODE_ENV !== "production",
});
GraphQL Yoga / graphql-httpで無効にする

Yogaには専用のプラグインがあり、graphql-httpではgraphql-jsのバリデーションルールを使えます。どちらもイントロスペクションのクエリを実行前に止めます。

// GraphQL Yoga
import { createYoga } from "graphql-yoga";
import { useDisableIntrospection } from "@graphql-yoga/plugin-disable-introspection";

export const yoga = createYoga({
  schema,
  plugins: process.env.NODE_ENV === "production" ? [useDisableIntrospection()] : [],
});

// graphql-http (or express-graphql)
import { NoSchemaIntrospectionCustomRule } from "graphql";

const validationRules = process.env.NODE_ENV === "production"
  ? [NoSchemaIntrospectionCustomRule]
  : [];
スキーマエクスプローラーのUIを本番環境から取り除く

GraphiQL、Playground、Apollo Sandboxは人がスキーマを探索するためのもので、公開された本番エンドポイントには不要です。認証チェックの背後に置くか、本番ビルドではまったくマウントしないでください。

ブロックリストより認可を優先する

イントロスペクションの無効化はハードニングの一手段であり、アクセス制御ではありません。すべてのフィールドには、リゾルバーレベルでの適切な認可が引き続き必要です。そうしないと、フィールド名を推測できた人は誰でもそれを呼び出せます。イントロスペクション対策は唯一の防御層ではなく、防御層の一つとして扱ってください。

FAQ

スキーマを隠すのは、単なる隠蔽によるセキュリティではありませんか?

部分的にはそのとおりです。執念深い攻撃者ならフィールド名を総当たりで推測できます。それでも、偵察を安価かつ完全なものにさせないことには価値があります。商用GraphQL APIのガイドやOWASPのGraphQLチートシートはいずれも、本番環境でのイントロスペクションの無効化を推奨しています。

開発者にスキーマを探索してもらいたい公開APIの場合はどうすればよいですか?

スキーマを意図的に提供してください。Schema Definition Languageファイルを公開する、ドキュメントサイトを用意する、または認証後にのみイントロスペクションを公開する、といった方法です。匿名のPOSTですべてを返すのとはまったく違います。

AppSafeはどのように検出していますか?

AppSafeは/graphqlと/api/graphqlに小さなイントロスペクションクエリをPOSTし、ステータス200で__schemaを含むレスポンスが返るかを確認します。返ってきた場合、「APIのセキュリティ」カテゴリで深刻度「中」として報告します。

あなたのアプリは大丈夫ですか?

AppSafeなら、この問題を含む数十項目を1回の無料スキャンでチェックできます。

アプリを無料でスキャン