登壇者の自己紹介

kuzushiki氏:それでは、発表を始めていきたいと思います。

まず簡単に自己紹介をさせてください。私は、kuzushikiと申します。現在、とあるセキュリティベンダーで診断員をやっていて、主にWebアプリケーションの脆弱性診断をしています。最近、バグバウンティに興味を持って、そういうものをちょっとやってみています。

“開発者フレンドリー”という特徴を持つ「GraphQL」

今回は「GraphQL」について話しますが、その前に、もうGraphQLを知っているよという方は、どのぐらいいるでしょうか?

(会場挙手)

ありがとうございます。けっこうな方が知っているということでですね、それではざっくりと説明したいなと思います。

GraphQLは簡単に一言で言うと、Facebook(現「Meta」)が開発したAPIのためのクエリ言語というところで、その特徴は、なんといってもデベロッパーフレンドリーというところで、こういった機能があります。

この画像はGraphQLの構造を示していて、GraphQLの「Graph」という名前から、ノードとエッジといった構造を取っています。

この例は、Shopノードに関連するProductノードを列挙するという操作で、特定のショップで取り扱っている商品を列挙することができます。こんな感じのクエリを送ります。

実際に、GraphQLのやりとりを見ていきましょう。こちらが、GraphQLのクエリとなっていて、users、id、usernameといったフィールドがあります。

usersには、このidという引数を渡していて、これは、idが1となるユーザーのIDとユーザー名を取得するという操作です。(スライドを示して)実際にこのようにレスポンスにもidとusernameが入っています。

「Introspection」の悪用方法とその対策

これでGraphQLの基本は以上にして、GraphQLには、デベロッパーフレンドリーな機能が本当にたくさんあります。その機能と悪用方法をセットで紹介していきたいなと思います。

まず、Introspectionですね。これはクエリに_schemaというようなフィールドを入れることによって、GraphQLサーバーで、どんなフィールドが有効かを取得できるものです。

実際にこういったクエリを送ると、このようにフィールドとしてpastesやpasteが得られます。

これはGraphQLのクライアントで、ドキュメントを表示するといったことに使えます。非常に便利な機能ではありますが、これは攻撃者にも実行できて、情報収集に使われてしまいます。

仮にフィールドを列挙した際に、systemUpdateのように明らかに管理用の操作がバレると、それを実行されてしまうおそれもあります。

この対策ですが、そもそもAPIの仕様を公開したくない場合は、この機能を無効にしておきましょう。一部、この機能を無効化できないサービスもあるので、そういった場合は、先ほどの特徴的なフィールド名である_schemaを、ブロックするのがよいのかなと思います。

「Alias」の悪用方法とその対策

では次にいきます。次は、Aliasですね。先ほどのusersというフィールドを複数実行したい時に、Aliasをつけないと、このように「Fields "users" conflict」というエラーが出てきます。

このエラーを防ぐために、user1、user2というAliasをここでつけています。これをすることでuser1とuser2のユーザー名を、1回のクエリで取得できます。

ではこれをどう悪用するのでしょうか。これは、認証回避やDoSに悪用されるケースがあります。

ここではGraphQLにログインの処理があるのを想定しています。そうすると、Aliasを使うことでパスワードのリスト攻撃みたいなことができてしまう。そのまま、攻撃者はすべてのフィールドに対する応答を取得できるので、(スライドを示して)このsuccessがtrueとなっているところが正しいパスワードであることがわかります。

それ以外にも、何回も実行することによるサービス停止も考えられます。

この対策ですが、1回にクエリに使えるAliasの数に制限を設けましょう。あと、GraphQLにはコストを計算する機能があるので、それで制限を設けましょう。これは、APIの回数制限では防げないので注意が必要です。

「Fragment」の悪用方法とその対策

次にいきます。次はFragmentですね。クエリ内で繰り返し使う部分を定義して使い回せるという機能で、この例でも、userInfoというFragmentを作っていて、それをスプレッド構文みたいな形式で使っています。これを実行することで、user1とuser2、それぞれのユーザー名とEメールを取得できます。

これをどうやって悪用するのかというと、こんな感じですね。Fragmentの中でFragmentが使えるケースがあります。

この例では、クエリ内でまず、AというFragmentを参照しているのですが、Fragment A内ではFragment Bを参照していて、BではまたAを参照しているので、ここで、無限ループに陥ってしまいます。これをCircular fragmentsと言います。

これは実は、すでに大半のGraphQLサーバーでは対策済みになっています。というのも、GraphQLの仕様でFragmentはサイクルを形成してはいけないと明記されているので、仕様どおりに実装できていればまったく問題ありません。

ただ、やはり一部脆弱な実装は存在していて、RubyのHTTPサーバーである「Agoo」でこの問題が発見されて、報告されています。現在は修正済みです。

「Field Suggestion」の悪用方法とその対策

続いて、Field Suggestionですね。これは、このようなクエリを送信した時に、エラーメッセージとして「Cannot query field "products" on type "Query". Did you mean "product"?」が出ます。

productsじゃなくてproductじゃないですかと、正しいフィールド名を提案してくれる非常にすばらしい機能なんですが、情報収集に使われてしまうよといったところで(お話しします)。

(スライドを示して)このように、攻撃者はそれっぽいフィールド名を……articles、documents、postsといったものを送信することで、エラーメッセージとして「Cannot query field "posts"」「Did you mean "pastes" or "paste"?」と返ってくるので「あぁ、じゃあ、これ使えるんだな」というのがわかってしまいます。

これは、Blind Introspectionと言うのですが、これを使った「Clairvoyance」というツールがあって、GraphQLサーバー側でIntrospectionが無効になっていても、このBlind Introspectionを使うことで、Introspectionと同様の情報を得ることができます。

この対策ですが、Introspectionをそもそも無効にするのであれば、Field Suggestionも、同様に無効化しておきましょう。

GraphQLのデベロッパーである、Lee Byron氏も「introspectionを無効にしたスキーマは、didYouMeanも無効になることを期待します。introspectionを無効にして、didYouMeanを有効にしたい理由やその逆は思いつかない」とコメントしています。自分もそのとおりだなと思います。

悪用に対する究極の対策「Persisted Queries」とは?

これまでに、たくさん悪用方法を紹介してきました。けっこういろいろな対策が必要だと思われた方もいると思います。ただ、1ついい方法があって、究極の対策と言われている、Persisted Queriesというものがあります。

本来攻撃者は、GraphQLサーバーにそのままそのクエリを送信できるのですが、このPersisted Queriesを使うと、攻撃者はそのクエリに対応するIDを送信することになります。その後GraphQLサーバー側のミドルウェアで、IDに対応するクエリを参照して、それを実行するということになるので、攻撃者が、自由にクエリを作ることができなくなります。

なので、これまでに紹介した攻撃をすべて防げます。これはもともとはネットワークの帯域節約のために考えられた技術なのですが、セキュリティにも有効ということで今回紹介しました。

不要な機能は無効化しておこう

「おわりに」ということで、GraphQL特有の機能における悪用方法を紹介しました。GraphQLはデベロッパーにとって、とてもフレンドリーなのですが、それは裏を返すと、ハッカーにとってもフレンドリーということです。不要な機能は無効化しておくことを推奨します。

というところで、参考文献を紹介しておきます。『Black Hat GraphQL』という本から今回紹介しました。約7,000円と、少しお高めですが、GraphQLの攻撃手法を体系的に学ぶことができるので、非常におすすめです。自分も学んだ結果、300ドルの賞金をもらうことができたので、実質プラスとなりました。

(会場笑)

その他の学習リソースもここで紹介しておきます。以上で発表を終了します。ありがとうございました。

(会場拍手)