KAKEHASHI Tech Blog

カケハシのEngineer Teamによるブログです。

【開発者でもできる!】OWASP ZAPを利用したGraphQLアプリケーションへの脆弱性診断

はじめに

こんにちは、LINE上で動くおくすり連絡帳 Pocket Musubi というサービスを開発している種岡です。 弊社では、ソフトウェアエンジニアそれぞれが得意分野をリードしながらも、フロントエンド、バックエンド、インフラを横断的に見ているケースが多いです。 プロダクトデリバリーに対して裁量が多く、やりがいが大きい一方で、安定運用に対しても責任が伴います。 とくに、薬局の薬剤師さんや患者さんが利用するというプロダクトの性質上、個人情報や機微な情報を取り扱うため、セキュリティに関しては開発時から意識せざるを得ません。 この課題に対して、チームでは開発中の段階から OWASP ZAP という脆弱性診断ツールを利用しています。 そこで、本記事ではOWASP ZAPツールの操作方法を紹介します。

OWASP ZAPについて

OWASP ZAPは脆弱性診断ツールで、以下のような特長があります。

  • オープンソースで、商用問わず無料
  • GUIアプリ(GitHub Actionもある)
  • Windows、Mac、Linuxとマルチプラットフォーム対応
  • 特定のURLに対する診断だけでなく、プロキシサーバーとして振る舞えるので、ブラウザで操作した画面に対して診断を行える
  • 短い開発サイクルで脆弱性診断を実行していきたいや取り敢えずスキャンしてみたいというニーズに合致
  • GraphQLサーバーに対しても診断が可能

システム全体像

脆弱性診断に関わるシステムの全体像はこちらになります。

architecture.png (566.1 kB)

電子おくすり手帳のアプリは、バックエンドにAppSyncを利用しています。 AppSyncのデータソース・認証モードは共にLambdaを紐付けしています。 OWASP ZAPからのリクエストは、AppSyncのデータソースLambdaまで届ける必要があります。(認証Lambdaでリクエストが止まってしまうと意図したテストにならないため) 後述しますが、そのためにLambda側で認証をすべて通す設定しておく必要があります。

OWASP ZAP側の設定

OWASP ZAPのインストール

  1. こちらから最新バージョンをダウンロードしてインストール
  2. 起動するとセッションの保存方法を問われるので、保存しないで進める スクリーンショット 2022-04-05 13.44.24.png (99.0 kB)
  3. アドオンの更新の確認ダイアログが出るのですべて更新しておく スクリーンショット 2022-04-05 13.44.52.png (320.1 kB)
  4. ホーム画面が無事表示される スクリーンショット 2022-04-05 13.48.49.png (301.5 kB)
  5. ターゲット以外に脆弱性診断リクエストが飛ばないように プロテクトモード にしておく protect-mode.png (40.1 kB)

認証ヘッダー付与スクリプトの登録

AppSyncには複数の認証モードがあり、認証後でないとデータソースへの参照が許可されません。 今回認証モードとしてLambdaを利用しており、クライアント側のリクエストには認証ヘッダーが必須になります。 デフォルトのOWASP ZAPのリクエスト内には認証ヘッダーがないため、スクリプト機能を使い実現させます。

  1. スクリプトタブを開きExtender配下にあるHTTP Senderを選択後、新規スクリプトボタンを押す owasp-prepare-1.png (1.1 MB)
  2. 新規スクリプトダイアログが表示されるので以下のように入力して保存 ※ 有効 にチェックを入れないとスクリプトの内容を反映してくれないので注意 owasp-prepare-2.png (95.7 kB)
  3. 保存するとテンプレートで選択していた AddZapHeader.js の中身が右側のカラムに表示されるので、今回付与させたいAuthorizationヘッダーの追加を記載する owasp-prepare-3.png (376.5 kB)

AppSync側の設定

すべてのリクエストを許可する認証Lambdaの作成

AppSyncのデータソースLambdaへリクエストを届けるための設定をします。 具体的には、以下の様にすべてのリクエストに対して isAuthorized: true を返すLambdaを作成します。 作成後に関数のARNを控えておきます。

lambda-authorizer.png (143.4 kB)

AppSyncに認証Lambdaを紐付ける

AppSyncのコンソールからデフォルトの認証モードをAWS Lambdaを選択し、控えておいたLambdaのARNを入力して保存します。

lambda-authorizer-2.png (422.4 kB)

脆弱性診断開始

脆弱性診断が開始されると大量のリクエストが発行されます。 そのため、サービスへの影響を考慮して本番環境ではなく、本番環境相当の環境に対して診断テストを実行するのが望ましいです。 また、負荷を考慮して、Lambdaのクオータを必要に応じて調整しておきます。

診断対象のエンドポイントをOWASP ZAPに登録

登録の仕方は GraphQLアドオンを利用する方法ZAPプロキシを利用する方法 です。

GraphQLアドオンはこちらの手順通りにやっていくとインストールできるとのことですが、インストール時に導入済みでした。

graphql-add-on.png (76.0 kB)

以下のようにGraphQL schema ファイルからのimportを選択し

excute-1.png (184.1 kB)

ファイルとエンドポイントURLを設定後に自動で診断対象を走査してくれます。

execute-2.png (115.6 kB)

しかし、走査後の対象リクエストを見ると、すべてのエンドポイントを網羅していなかったりリクエスト自体のフォーマットが不適切 で400が返ってきてしまうものがあったりと、懸念事項が多いため利用を断念しました。

もう1つのZAPプロキシでのリクエスト登録方法について記載します。

  1. ツールオプションローカル・プロキシ と進みプロキシサーバー用のポートを設定します ※ポートの重複に注意してください zap-proxy.png (939.5 kB)
  2. 設定が反映されているかどうかは画面下部で確認できます zap-proxy-2.png (868.7 kB)
  3. local環境からcurlコマンドを利用して対象を登録していく GraphQLクライアントには、cURL用にコマンドを出力してくれる機能ものがあるので、それを利用すると便利です。 私はAltair GraphQL Clientというツールを利用しています。 curlコマンド実行の際には、ZAPプロキシを経由してリクエストを実行するように -x オプション を追加します。 また、-k オプションを追加して証明書エラーを無視するように制御しておきます。 terminal curl -x http://localhost:8089 -k ... 無事リクエストを登録し終えると以下のようになります。 target-list.png (309.2 kB)
  4. コンテキストに追加して攻撃対象に設定する。 add-context.png (1.1 MB)

    add-context-2.png (157.3 kB)

動的スキャンの前にGraphQLオプションを修正

ツールオプションGraphQL で以下の部分を更新しておきます。 この設定をしておくことで、テスト時にvariablesの部分に悪意のあるパラメーターを動的に埋め込んでくれるようになります。

graphql-option.png (559.4 kB)

動的スキャンの実行

scan.png (435.9 kB)

動的スキャン実行前に入力ベクトルの設定を以下に変更します。 これにより、POSTデータをJSONとして解析し、queryという名前のパラメータを無視して、variables部分だけを攻撃するようになります。 また、 Enabled Script Input Vectors をオフにすることで、query文のインライン引数を書き換えてしまうInput Vectorsスクリプトを無効にして、query文自体はそのままにします。

scan-2.png (529.2 kB)

実行結果の確認

診断が実行されて大量のリクエストが発行されます。 queryとmutationのリクエストの中身を確認すると以下のようにvariablesの部分を動的に変更し攻撃していることが確認できました。

mutation-result.png (34.6 kB)

query-result.png (37.1 kB)

おわりに

OWASP ZAPという脆弱性診断ツールを使い、簡単にアプリケーションの脆弱性の有無を確認することができました。 一方で、攻撃手法は日々進化しているため、1度限りの利用は好ましくなく、定期的に診断を繰り返しておく運用が望ましいと思います。 また、セキュリティリスクは脆弱性診断ツールで検知すれば良いというスタンスではなく、開発中の段階で意識してコーディングすることが重要だと感じます。 GraphQLのセキュリティ対策として良く挙げられる以下の項目は開発着手前にチームで勉強会を開くなどして意識すると良さそうに思いました。

  • リクエストやリゾルバ毎にリクエストのタイムアウトを設定
  • 巨大なクエリの発行を防ぐために、取得できる件数に制限を入れる
  • 深さや複雑度が大きいクエリの検知

この記事で少しでも、電子おくすり手帳やLINEを使った患者さんとのコミュニケーションツールに興味を持って頂けたら幸いです。