Retty Tech Blog

実名口コミグルメサービスRettyのエンジニアによるTech Blogです。プロダクト開発にまつわるナレッジをアウトプットして、世の中がHappyになっていくようなコンテンツを発信します。

GraphQL Inspection で守る GraphQL API

こんにちは。Retty インフラチームの幸田です。

今回は Retty で利用している GraphQL API に WAF (Web Application Firewall) を導入したのでその話をしようと思います。

Retty と GraphQL API

下記記事の「システム構成」にて紹介されているように、フロントエンドからバックエンドのマイクロサービスに通信するにあたり、BFF サーバとして GraphQL を採用しています。

engineer.retty.me

2年ほど前の記事ですが2023年3月現在も全体の構成は大きく変わらず、着々とマイクロサービス化を進めています。

GraphQL を利用した攻撃

ウェブアプリケーションに対する攻撃には様々なものが存在しますが、GraphQL API への攻撃というとあまり想像がつかないかもしれません。

下記ブログでは GraphQL API脆弱性を3つのカテゴリーに分けて解説されています。

www.fastly.com

以下、重複する箇所もありますが、どのようなものがあるのか簡単に紹介します。

リスクを伴う設定

直接的な脆弱性ではないものの、リスクを伴う設定について取り上げられています。代表的なものとして、GraphQL のスキーマ情報を取得することができるイントロスペクションデバッグモードによるスタックトレースの表示などが挙げられます。

これらは開発時には便利なものですが、本番環境でも有効化すると攻撃者に情報を与えることに繋がります。

悪意のあるクエリ

こちらは GraphQL 特有の事情を悪用した攻撃について触れられています。
GraphQL は利用者側でクエリを作成することで任意の情報を取得可能ですが、それを悪用して指数関数的に負荷のかかるネストされたクエリを投げる方法が一例として紹介されています。

Web API脆弱性

OS コマンドインジェクションなどは有名な攻撃かと思いますが、それらは GraphQL でも起こりうるということがコードの例と共に紹介されています。

Fastly Next-Gen WAF について

Fastly Next-Gen WAF は、Fastly より提供されている WAF です。元々は Signal Sciences と呼ばれていました。

Retty では、2021年の秋頃にメインのサービスである Retty を含むいくつかのシステムに導入しました。
下記ブログはその年のアドベントカレンダーで執筆したもので、Next-Gen WAF のコンセプトや主要機能、導入方法に加えて簡単な利用方法などを書いているのでよければご覧ください。

engineer.retty.me

また同年に開催されたイベントである Yamagoya 2021 に登壇した際の資料は下記です。

GraphQL Inspection

GraphQL Inspection は Next-Gen WAF より提供されている機能です。先ほど紹介した GraphQL API 対して送られる様々な攻撃を検出し、必要に応じてブロックを行うことができます。

WAF のセットアップ

Next-Gen WAF を利用するためには、エージェントのセットアップと WAF 自体の設定が必要ですが、この記事ではエージェントのセットアップ手順は省略します。選択可能なエージェントのインストールタイプや Pros / Cons については過去の登壇資料公式ドキュメントをご覧ください。

GraphQL Inspection の有効化

エージェントのセットアップが完了し、Next-Gen WAF が利用できる状態になったので WAF 自体の設定を行います。クロスサイトスクリプティングSQL インジェクションなど主要な攻撃は特にセットアップがなくとも検出可能ですが、GraphQL API の場合には少し設定が必要になります。

Templated Rule

Templated Rule はルールのテンプレートのようなもので、CVE をはじめとした様々な攻撃に対して用意されており、必要な項目を埋めるだけで簡単に攻撃を検出(+ 種類によってはブロックのオプションも有り)することができます。

例えばログインの失敗を検出するための Templated Rule である Login Failure を例に取ると、ログイン時に利用するページのパスやステータスコードを入力するだけで検出やブロックが可能になります。

Templated Rule の Login Failure の例

GraphQL 向けには、現在は下記2種類の Templated Rule が存在します。

  • GraphQL API Query
  • GraphQL IDE

GraphQL API Query は GraphQL Inspection を利用するにあたって必須となるルールです。
GraphQL IDE は任意で設定するもので、GitHub のエクスプローラーのように Web 上で動作させる IDE が存在する場合に設定が推奨されるとのこと。Retty ではそのようなサービスは提供していないので今回は利用していません。

GraphQL API Query の設定値は次のようになっており、基本的には GraphQL API のエンドポイントと対象のメソッドを指定するのみです。

GraphQL API Query の設定画面

設定を有効化すると GraphQL Inspection が有効になり、GraphQL のリクエストを検出することができるようになります。
検出後のリクエストがどのように分類されるのかについては、こちらの記事で紹介しています。

動作確認

下記は GraphQL API のエンドポイントに対して、スキーマ情報を取得するための Introspection Query が投げられた例です。リクエストの内容が GraphQL であることを示す GraphQL API Query と、Introspection Query であることを示す GraphQL Instrospectionシグナルが付与されています。

GraphQL の Signal が付与されている様子

下記は SQL インジェクション のペイロードを含む GraphQL のリクエストを送信した様子です。SQL Injection であることを示す SQLI が付与されています。

SQLi を検出している様子

リクエストされたクエリに応じてそれぞれのシグナルを付与していることから、事前に設定したパスやメソッドだけで判別しているのではなく、リクエスト内容が解析されていることが分かります。

ブロックについて

以上の設定で攻撃が検出できるようになったので、続いてはブロックの設定です。

Site Alerts を用いた攻撃のブロック

Site Alerts は、リクエスト回数に基づくしきい値をベースに該当の IP アドレスをフラグするための機能です。フラグされた IP アドレスをベースにブロック設定などを行うことが可能です。
詳細は以前のブログにあるため省略しますが、下記シグナルに該当する攻撃については、特に設定を行わずとも、デフォルトでしきい値ベースのブロックが行われるようになっています*1。それぞれのシグナルの意味はドキュメントをご覧ください。

  • Attack Tooling
  • AWS SSRF
  • Backdoor
  • CMDEXE
  • SQLI
  • Traversal

例えば、動作確認で示した「SQL インジェクションのペイロードを含む GraphQL のリクエスト」については、SQLI シグナルを含むリクエストに該当するため、自動的にしきい値ベースのブロックが有効になります。

シグナルを用いたブロック設定

リクエスト内容に応じて付与されるようになった GraphQL Max DepthGraphQL Introspection などのシグナルを利用してブロックの設定(即時ブロック)を行うことができます。

設定はとてもシンプルで、GraphQL Max Depth をブロックしたい場合には次のようなルールを作成することで実現できます。

GraphQL Max Depth をブロックするルールの例

また前述した Site Alerts を用いて、GraphQL Max DepthGraphQL Inspection シグナルに対しても、しきい値ベースのブロック設定を行うことも可能です。ただし、この設定については Premier platform の契約が必要になります。
これにより、即時ブロックだけでなく「GraphQL Max Depth シグナルを持つリクエストが x 分で x 回あったらブロックする」といったことが実現できます。

URL パスと HTTP メソッドを利用したブロック設定

GraphQL API は単一のエンドポイントに対して POST メソッドでリクエストを送るのが一般的かと思います。*2
このようにリクエストされるエンドポイントやメソッドが固定されている場合、下記に該当するようなリクエストは、全て 正常でない とみなすことができます。攻撃を含んでいるかどうかは別の話ですが、少なくともアプリケーションサーバまで到達させる必要のないリクエストです。

  • 特定のエンドポイント (/graphql など) 以外へのリクエス
  • POST メソッド以外のリクエス

そこで、下記スクリーンショットに示すようなカスタムルールを作成すると、アプリケーションに到達させるリクエストを必要最低限にすることができます。

カスタムルールの例

上記のルールは下記条件のリクエストを拒否するルールです。

  • パスが /graphql で POST メソッド以外のもの
  • パスが /graphql で OPTIONS メソッド*3以外のもの
  • パスが /health-check *4で GET メソッド以外のもの

もちろんこれ以外にも様々な条件を付け加えることができます。

ブロック設定のまとめ

これらの設定により、以下のようなリクエストをブロックできるようになりました。

  • SQLI (SQL インジェクション) や CMDEXE (コマンドインジェクション) などの攻撃を含むリクエストが Site Alerts を利用して、しきい値ベースでブロック可能になった
  • GraphQL Max Depth など GraphQL を利用した攻撃と思わしきリクエストが Custom Rule を用いてブロック可能になった
  • 予め想定されているパス、メソッド以外へのリクエストが Custom Rule を用いてブロック可能になった

今回は簡単なブロック設定の例を紹介しましたが、様々な条件を付け加えることによって更に必要最低限なリクエストのみに絞ることも可能です。

最後に

Fastly Next-Gen WAF の GraphQL Inspection を利用して、GraphQL API を保護する方法を紹介しました。
まだ導入して間もないのでリクエスト傾向を見ながらですが、より安全に運用できるように更なるチューニングを重ねていきたいです。

一緒にやってくれる方も探しています!

hrmos.co

*1:ブロックモードの時のみ

*2:Persisted Query など GET を利用する例外を除く

*3:CORS の preflight で使用する

*4:前段の sigsc-agent のヘルスチェックで使用する