GraphQLは「設計図を配って歩く」のか?:インスペクション無効化と防御の実践
現場でコードレビューをしていると、GraphQLの利便性に溺れて「セキュリティの基本」を忘れているエンジニアによく遭遇する。GraphQLは強力だ。フロントエンドが必要なデータを過不足なく取得できる。だが、その恩恵の裏側で、「スキーマ・インスペクション(Introspection)」という名の地図を、攻撃者に無料で配っていないか?
今回は、XSSの文脈と絡めつつ、GraphQLが抱える「情報の非対称性」という脆弱性と、それを物理的に遮断するための実戦的な防御策を解説する。
—
1. なぜ「インスペクション」が命取りになるのか
Introspection Queryとは、GraphQLサーバーに対して「どんなクエリが投げられるのか?」「どんな型があるのか?」を問い合わせる機能だ。開発環境では神機能だが、本番環境でこれが生きていれば、攻撃者は君たちのアプリの全貌を数秒で把握できる。
攻撃者の視点:PoC(概念実証)
攻撃者はまず、以下のクエリを投げてくる。
攻撃者が送るIntrospection Query
query {
__schema {
types {
name
fields {
name
description
type { name }
}
}
}
}
これが通れば、君たちのDB構造、利用可能なミューテーション(データの更新処理)、さらには隠しパラメータまで丸裸だ。この情報があれば、脆弱なエンドポイントを特定し、そこへXSSペイロードを流し込む計画を立てるのは容易い。「何があるか分からない」という防御壁は、Introspectionによって崩壊する。
—
2. 実装レベルでの防御:スキーマの「隠蔽」
最も手っ取り早く、かつ確実なのは「本番環境でのIntrospectionの無効化」だ。
Node.js (Apollo Server) の例
Apollo Serverを使っているなら、設定一つで閉じられる。環境変数を見て制御するのが鉄則だ。
const { ApolloServer } = require(‘apollo-server’);
const server = new ApolloServer({
typeDefs,
resolvers,
// 本番環境のみIntrospectionを無効化する
introspection: process.env.NODE_ENV !== ‘production’,
// Playground(ブラウザで試せるやつ)も本番では閉じる
playground: process.env.NODE_ENV !== ‘production’,
});
—
3. 「認可」の欠如がXSSを招く
Introspectionを消しても安心はできない。GraphQLは単一エンドポイントであることが多く、認証・認可のチェックを各リゾルバ(Resolver)内に記述し忘れると、「誰でも任意のデータを操作できる」状態になる。
例えば、ユーザープロフィールを更新するミューテーションで、XSSペイロードがそのまま保存されてしまうケース。
NGな実装例(脆弱なリゾルバ):
// 誰でも実行できてしまうし、サニタイズもない
updateProfile: (_, { bio }, context) => {
return db.users.update(context.userId, { bio });
}
OKな実装例(認可と検証を組み込む):
// 認可チェックをリゾルバの先頭で必ず行う
updateProfile: (_, { bio }, context) => {
if (!context.user) throw new AuthenticationError(‘ログインしてください’);
// HTMLタグを無効化する処理を入れる(XSS対策)
const sanitizedBio = bio.replace(/<[^>]>?/gm, ”);
return db.users.update(context.userId, { bio: sanitizedBio });
}
—
4. インフラ層での「ダメ押し」
アプリケーションコードの修正は必須だが、インフラ側でも「念には念を」入れるのがプロの仕事だ。NginxやWAFで特定のクエリパターンを弾く設定も検討しよう。
Nginxでのリクエスト制限例:
Introspection Query特有のキーワード __schema や __type が含まれるリクエストを、本番環境のパスに対して拒否する設定だ。
GraphQLエンドポイントへのIntrospection Queryをブロック
location /graphql {
if ($request_body ~ “__schema”) {
return 403;
}
proxy_pass http://app_server;
}
—
最後に:セキュリティは「設定」ではなく「文化」だ
GraphQLは便利だが、その柔軟性が「隠すべき情報」までをも公開させてしまう。
1. 本番環境でIntrospectionは絶対にオフにする。
2. すべてのリゾルバに認可ロジックを強制する。
3. 入力データはGraphQL越しであっても、必ずXSS対策(エスケープ・サニタイズ)を施す。
「まあ動いているからいいか」という妥協が、数ヶ月後のインシデントを生む。君たちが書くその一行のリゾルバが、プロダクトのセキュリティの最後の砦になることを忘れないでほしい。
現場からは以上だ。何か詰まったらまた相談してくれ。
コメント