玄関の鍵をかけたはずが…?GraphQLの「無限の迷路」からサーバーを守る話
こんにちは。セキュリティの世界で「何が起きているか」を紐解くのが大好きなエンジニアです。
今日は、最近のモダンな開発現場でよく使われる「GraphQL」という技術に潜む、ちょっと厄介な落とし穴についてお話しします。XSS(クロスサイトスクリプティング)のような「悪意あるコードを埋め込まれる」攻撃とは少し毛色が違い、こちらは「あまりに親切すぎて、逆に押しつぶされる」という性質の攻撃です。
まるで、泥棒が「家の中を全部見せて!」と言って、家中の隠し扉をすべて同時に開けさせ、家主をパニックに追い込むようなイメージですね。さあ、一歩ずつ対策を学んでいきましょう!
—
1. GraphQLの「再帰的クエリ」って何?
GraphQLは、クライアントが「欲しいデータだけ」をピンポイントでリクエストできる便利な仕組みですよね。例えば「ユーザーの名前」と「そのユーザーが投稿した記事」が欲しい時、一度の通信で取得できます。
しかし、ここに攻撃者の狙いがあります。GraphQLは「自分の中に自分を含む」構造(再帰的な構造)を作ることができてしまうのです。
「無限の迷路」の例え
想像してみてください。あなたの家の玄関先に、何でも言うことを聞いてくれる執事が立っているとします。
- あなた: 「友達の情報を教えて」
- 執事: 「はい、友達のAさんです」
- 攻撃者: 「その友達の友達を教えて。そのまた友達の友達を教えて…(これを100回繰り返す)」
執事は律儀にそのリストを作り続けます。家の中のすべての部屋を何度も何度も往復するうちに、執事は疲弊して倒れ、家は機能不全に陥ります。これが「GraphQLの再帰的クエリによるDoS攻撃(サービス拒否攻撃)」の正体です。
—
2. なぜこれが危険なのか?
通常のAPI(REST APIなど)であれば、エンドポイントごとに処理が決まっているので、サーバーがどれくらい負荷を受けるか予測しやすいんです。しかし、GraphQLは「クエリの複雑さ」をクライアントが自由に決められてしまいます。
攻撃者は、この仕組みを悪用して極端に深くネストした(入れ子になった)クエリを送信します。これを処理するためにサーバーのCPUやメモリが食いつぶされ、本物のユーザーがサービスを利用できなくなってしまうのです。
—
3. どうやって防ぐ?「クエリ深度制限」という門番
この攻撃への特効薬は、「クエリの深さに制限をかけること(Query Depth Limiting)」です。
「どれだけ深く掘り下げても、最大で3階層までね!」というルールを設けるわけです。玄関先で執事に「3回以上の深掘りは断っていいよ」と指示しておくようなものですね。
実装のヒント(JavaScript/Node.js環境の例)
多くのGraphQLライブラリでは、これを簡単に設定できるプラグインが用意されています。例えば graphql-depth-limit というツールを使うと、こんな風に書けます。
import depthLimit from ‘graphql-depth-limit’;
// サーバーの構成設定
const server = new ApolloServer({
typeDefs,
resolvers,
// 検証ルール(Validation Rules)に深度制限を追加
validationRules: [
// 最大深度を「3」に設定(これより深いクエリは拒否される)
depthLimit(3, {}, (depths) => {
console.log(クエリの深さ: ${depths});
})
],
});
これだけで、深すぎるリクエストが来た瞬間にサーバーは「ごめん、深すぎるから答えられないよ」と即座に拒否できるようになります。
—
4. さらに一歩先へ:「クエリコスト制限」
深さを制限するだけでは足りない場合もあります。例えば、深さは浅くても「1回のクエリで100万件のデータを取得しろ」というような、重たいリクエストを送る攻撃も考えられますよね。
そこでプロの現場では、「クエリコスト(Query Cost)」という考え方を使います。
- ユーザー情報の取得は「コスト1」
- 全投稿一覧の取得は「コスト10」
- …といった具合に、各項目に重み付けを行い、「1回のクエリの合計コストが50を超えたら拒否する」という防衛線を張ります。
—
まとめ:セキュリティは「バランス」
セキュリティ対策は、ガチガチに固めすぎると利便性が損なわれます。しかし、今回のような「再帰的なクエリ制限」は、ユーザーの利便性を下げずに、サーバーを守るための非常に効率的な防壁です。
- まずは「深さ制限」から始める: 簡単に導入できて、効果絶大です。
- 次に「コスト制限」を検討する: サービスが成長し、APIが複雑になったら導入しましょう。
「便利だからこそ、限界を決めておく」。これが、現代のWebアプリケーションを守るための鉄則です。皆さんの開発しているサービスでも、ぜひ一度「どれだけ深いクエリが通ってしまうのか?」を実験してみてください。
それでは、また次の現場でお会いしましょう!安全な開発ライフを!
コメント