【入門編】GraphQL の再帰的クエリによるDoS攻撃(クエリ深度制限) – アプリケーションセキュリティ & 安全な開発防御ガイド

玄関の鍵をかけたはずが…?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アプリケーションを守るための鉄則です。皆さんの開発しているサービスでも、ぜひ一度「どれだけ深いクエリが通ってしまうのか?」を実験してみてください。

それでは、また次の現場でお会いしましょう!安全な開発ライフを!

コメント

タイトルとURLをコピーしました