公開された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回のリクエストに変えてしまいます。
直し方
NODE_ENVがproductionのときはイントロスペクションをオフにします。ローカルではオンのままにして、チームがSandboxでスキーマを確認できるようにします。
import { ApolloServer } from "@apollo/server";
const server = new ApolloServer({
typeDefs,
resolvers,
introspection: process.env.NODE_ENV !== "production",
});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]
: [];GraphiQL、Playground、Apollo Sandboxは人がスキーマを探索するためのもので、公開された本番エンドポイントには不要です。認証チェックの背後に置くか、本番ビルドではまったくマウントしないでください。
イントロスペクションの無効化はハードニングの一手段であり、アクセス制御ではありません。すべてのフィールドには、リゾルバーレベルでの適切な認可が引き続き必要です。そうしないと、フィールド名を推測できた人は誰でもそれを呼び出せます。イントロスペクション対策は唯一の防御層ではなく、防御層の一つとして扱ってください。
FAQ
スキーマを隠すのは、単なる隠蔽によるセキュリティではありませんか?
部分的にはそのとおりです。執念深い攻撃者ならフィールド名を総当たりで推測できます。それでも、偵察を安価かつ完全なものにさせないことには価値があります。商用GraphQL APIのガイドやOWASPのGraphQLチートシートはいずれも、本番環境でのイントロスペクションの無効化を推奨しています。
開発者にスキーマを探索してもらいたい公開APIの場合はどうすればよいですか?
スキーマを意図的に提供してください。Schema Definition Languageファイルを公開する、ドキュメントサイトを用意する、または認証後にのみイントロスペクションを公開する、といった方法です。匿名のPOSTですべてを返すのとはまったく違います。
AppSafeはどのように検出していますか?
AppSafeは/graphqlと/api/graphqlに小さなイントロスペクションクエリをPOSTし、ステータス200で__schemaを含むレスポンスが返るかを確認します。返ってきた場合、「APIのセキュリティ」カテゴリで深刻度「中」として報告します。