動的アプリケーションセキュリティテスト (DAST) の拡張について
はじめに
マイクロソフトのエンジニアリングチームは、セキュリティ開発ライフサイクルを使用してマイクロソフトの Secure Future Initiative(セキュリティ原則:Secure by Design、Secure by Default、Secure Operations)に則り製品を開発しています。セキュリティ開発ライフサイクルの重要な要素のひとつは、敵対者がセキュリティの脆弱性を悪用する前に、セキュリティの脆弱性を発見し軽減することを目的としたセキュリティテストです。多くの企業では、コードレビュー、ペネトレーションテスト、レッドチームなどの手動プロセスや、静的アプリケーションセキュリティテスト(SAST)などの自動化プロセスに対応した確立されたソリューションを導入しています。しかし、API Webサービスの保護に関しては、動的アプリケーションセキュリティテスト(DAST)に苦労することがあります。これは、SAST ツールはサービスの開発者が追加作業をしなくても DevSecOps チームがサービスの CI/CD パイプラインに簡単に統合できるのに対し、DAST ツールの統合は困難だからです。
最近の BlueHat にて、マイクロソフトの Azure Edge & Platform チームが、DAST を大規模に実行するためのセキュリティ自動化について講演しました。本ブログでは、その講演を振り返ります。この取り組みは、マイクロソフト自身が作成、所有、管理する、つまりファーストパーティのアプリケーションまたはサービスのポートフォリオ全体で、数千の内部および外部の API Web サービスを対象としています。
多くの企業が DAST の拡張に課題を抱えている理由
DAST は、ソフトウェアの実行時にテストをすることで、セキュリティの脆弱性または弱点を引き出そうとするソフトウェアのテストプロセスです。Web アプリケーションや Web サービスのテストでは、通常、ファジングなどの手段を使用して不正な形式の Web 要求を送信し、要求に対する Web サーバーの応答に基づいてバグを特定します。Web アプリケーションのスキャンに利用できる多くの優れた商用およびオープンソースの DAST ツールがあり、マイクロソフトの RESTler もその1つです。Web サーバーをスキャンする場合、利用する DAST ツールの数によらず、ツールは Web エンドポイントを認識する必要があり、Web サーバーのエンドポイントロジックはリクエストを処理する必要があります。また、Web サービスの API 仕様と有効な認証情報にアクセスする必要があります。つまり、安全な方法で大規模にテストするには、開発者が手動で作業する必要があります。
Web エンドポイントの検出
従来、DAST ツールに必要な入力は、スキャンする対象である Web サーバーのアドレスのみでした。DAST ツールは、ホームページから特定のサイトを自動的にクロールし、HTML に埋め込まれたリンクをたどってすべてのサイトページを検出します。これにより、DAST ツールは、ユーザーからの入力を受け入れるページ、つまり URL のコンテキストで、サイトが提供するすべての動的機能を検出します。
上記の図で示す例では、DAST ツールはホームページ https://www.contoso.com からリンクをたどり、クロールを開始します。https://www.contoso.com/contact.html の URL パラメーターやリクエストボディを介してユーザーからの入力を受け付ける問い合わせフォーム https://www.contoso.com/contact/inquiry.asp を発見します。DAST ツールは、動的なページにおける入力処理のセキュリティ脆弱性を発見するために、不正な値でページにリクエストを送信します。
一般に、このようなクロールの方法は、従来の Web サイトでは十分に機能します。しかし、機能が API を介してのみ公開される Web サービスでは使用できません。次の図では、モバイルアプリが REST API Web サービス https://api.fabrikam.com/ と通信します。
この例では、モバイルアプリには、REST API エンドポイント /login、/products、/cart、/logout がアプリにハードコードされています。DAST スキャナーが https://api.fabrikam.com/ を開こうとすると、エラーが発生します。
このシナリオに対応するため、最新の DAST ツールでは、OpenAPI 仕様(Swagger 仕様)としてユーザーはエンドポイントのリストを入力します。この仕様は、Web サービスの API URL の詳細なリスト、各 API で受け付けるすべてのパラメーター、および各パラメーターの予想される格式をメタデータで列挙します。OpenAPI 仕様では、NSwag および swagger-core をパッケージで生成します。パッケージを使用するには、特定のサービスのソースコードと作業ビルド環境の両方にアクセスする必要があります。そのため、開発者はソリューションを Web サービスプロジェクトに手動で統合する必要があります。これは小規模な組織にとっては良いアプローチかもしれません。
しかし、企業のセキュリティチームが自社の開発者全員にソリューションを Web サービスプロジェクトに統合するように指示した場合、各プロジェクトのコードとビルド構成を手動で更新する必要があります。このプロセスには莫大なコストがかかるため、経営層や開発者から反発を受けることは間違いありません。
拡張性のある OpenAPI 仕様の自動生成ソリューション
OpenAPI 仕様では、サービスごとの開発者による手作業が不要な、自動的に生成する手法がいくつかあります。
1 つの解決策は、対象の Web サーバーに送信されたリクエストを監視し、リクエストに基づいて OpenAPI 仕様をリアルタイムで推定する方法です。この監視は、クライアント側、サーバー側、または API ゲートウェイ、ロードバランサーなどの中間で実行できます。つまり、各開発者の作業を必要としない、スケーラブルで自動化することができるソリューションです。しかし、このアプローチでは、実行時間によっては、すべての Web エンドポイントを包括的に識別できない可能性があります。たとえば、/logout エンドポイントを呼び出したユーザーがいない場合、/logout エンドポイントは OpenAPI 仕様によって自動生成されません。
他の解決策は、Web サービスのソースコードを静的に分析し、自動的にソースコードから取得できる定義済みの API エンドポイントルートに基づいて OpenAPI 仕様を生成することです。マイクロソフトは、このソリューションのプロトタイプを社内で作成し、動作するビルド環境にアクセスせずに抽象的な構文ツリーを解析しました。しかし、すべての API エンドポイントルートおよびパラメーターを確実に検出することは困難でした。また、このソリューションでは、動的に登録された API ルートエンドポイントハンドラーのシナリオは対応できません。加えて、マイクロソフトは、LLM を使用してソースコードから OpenAPI 仕様を派生させる静的分析ソリューションを社内で検討しました。いくつかの有望な結果は得られましたが、残念ながら、解決策は非決定論的でした。
何千もの Web サービスに対して DAST を完璧に拡張するには,OpenAPI 仕様を自動的に、包括的に、そして決定論的に生成する必要があります。このようなソリューションを導入した場合でも、テストしたサービスの機能を DAST ツールが実行できることを確認する必要があります。
認証と認可
ほとんどのエンタープライズ Web サービスでは認証が必要なため、Web サービスの全機能を実行するには DAST ツールに認証情報を提供する必要があります。また、Web サービスは、DAST ツールがサービスに要求を送信するための権限を付与する必要もあります。テスト環境では、通常、サービスごとに一意のテストアカウントを使用するか、包括的なテストアカウントを使用して DAST ツールを構成します。前者は、サービスごとに DAST ツールの実行を調整する必要があるため非常に複雑であり、後者は妥協点です。どちらの方法も、各サービス所有者が手動で構成を変更する必要があります。同様に、OpenAPI 仕様の生成について、経営層と開発者に莫大なコストがかかります。
拡張性のある DAST ソリューション
クラウドサービスプロバイダーとして、マイクロソフトは Web サービスを実行する IaaS と PaaS プラットフォームを所有しています。このプラットフォームは、ファーストパーティサービスのインベントリを生成し、サービスにカスタムセキュリティツールを展開することができます。マイクロソフトのエッジコンピューティングとオペレーティングシステムを担当する Azure Edge & Platform チームは、すべての IaaS ベースおよび PaaS ベースの Web サービスの非運用のテストインスタンスに展開できるエージェントを開発しました。このエージェントは、デプロイ機能によって、ソリューションを作成します。非運用インスタンスは、運用インスタンスと同じコードを共享します。非運用環境で DAST を実行することで、運用環境のデータを確実に保護します。DAST エージェントは、大規模で統合された DAST プラットフォームの1つのコンポーネントです。Web サービスのビルド環境にアクセスする必要がなく、開発者が新機能をサービスの CI/CD パイプラインに手動で統合する手間を省くことができます。エージェントは、Web サービスの実行中のプロセスに完全にアクセスできるため、メモリを検査し、新しいコードを実行中のプロセスにロードすることができます。
Web エンドポイントの検出
一般的な Web サーバーは、受信した要求を管理するため、URL ルートとルートハンドラーのマッピングを維持します。エージェントは、実行時に Web サーバーのプロセスのメモリを検査してルートマッピングを検出し、検出されたマッピングから OpenAPI 仕様を生成することで、このアーキテクチャを活用します。
多くのマイクロソフトのWebサービスは ASP.NET で書かれており、リクエスト処理パイプラインを使用して受信した要求を処理します。
上図の各ブロックは、ミドルウェアコンポーネントです。パイプライン内の次のコンポーネントに渡す前に、リクエストを処理します。(または残りのミドルウェアコンポーネントを短縮します。)
パイプラインの最後のミドルウェアコン�ポーネントは、エンドポイントコンポーネントです。Microsoft.AspNetCore.Builder.EndpointRoutingApplicationBuilderExtensions.UseEndpoints() のドキュメントには、「Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware は、ルーティングの決定が行われるミドルウェアパイプライン内のポイントを定義し、エンドポイントが HttpContext に関連付けられる。」と記載されています。Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware のソースコードを調べると、_endpointDataSource というフィールドがあります。これは、Microsoft.AspNetCore.Routing.EndpointDataSource オブジェクトで、それ自体にエンドポイントというプロパティがあります。ドキュメントには、エンドポイントプロパティは「読み取り専用のエンドポイントインスタンスコレクションである」と記載があります。各エンドポイントインスタンスには、メタデータ収集が含まれており、ルートエンドポイントの詳細を保持しています。
Web エンドポイントを検出するためのソリューションでは、エージェントが実行時に Web サーバープロセスのメモリ内のリクエスト処理パイプラインを自動的に検出し、上記のオブジェクトフィールドに従ってすべてのエンドポイントオブジェクトインスタンスを検索します。その後、自動化されたエージェントは、各インスタンスに関連付けられたメタデータを処理し、Microsoft.OpenApi.Writers.OpenApiJsonWriter を使用して、すべての API エンドポイントと詳細なパラメーターを含む Web サービスの完全な OpenAPI 仕様を動的に生成します。OpenAPI 仕様は、対象の Web サービスをスキャンするための DAST ツールへの入力として使用できます。
上記の詳細は ASP.NET Core 固有のものですが、ランタイムメモリインスペクションによるルート検出の同じアプローチは、ASP.NET Framework、PHP、Node.js などの他のフレームワークで記述された Web サービスでも活用することができます。
認証と認可
テストアカウントを使用して DAST ツールが認証されたリクエストを Web サービスに送信することには、課題とリスクがあります。これを解消するために、実行時に認証および認可を行うミドルウェアコンポーネントをフックします。これにより、テストアカウントが不要になります。
認証と認可の課題を解決するための手法は、Transparent Auth と呼ばれます。この手法では、Authentication コンポーネントと Authorization コンポーネントの前に新しいデリゲートを挿入します。これにより、エージェントがメモリ内のリクエスト処理パイプラインを自動的に探索し、実行時に新しいデリゲートを挿入します。
Transparent Auth は ASP.NET Core でのみ利用できますが、実行中に認証と認可をフッキングする同じアプローチは、ASP.NET Framework、PHP、Node.js などの他のフレームワークで記述した Web サービスでも活用できます。
認証フック
パイプラインの認証コンポーネントの前に挿入するフックにて、クライアントのリクエストを検査して、統合された DAST ツールからのリクエストかどうかを判断します。プロセス外のコンポーネントを使用して、統合された DAST ツールから送信されたすべてのリクエストを追跡し、フックはそのコンポーネントと通信して、リクエストが実際に統合された DAST ツールから発信されたものであることを確認します。フックが統合された DAST ツールからのリクエストではないと判断した場合、リクエストを元の認証コンポーネントに渡し、元の認証コンポーネントがリクエストを通常どおり処理します。
一方、フックが統合された DAST ツールからのリクエストであると判断した場合、追加の作業を行います。元の認証コンポーネントをスキップするだけでは、他のミドルウェアコンポーネントが要求に関連付けられた ID を使用すると問題が発生する可能性があり、スキップはできません。例えば、認証されたリクエストに「Hello,
リクエストのコンテキストが新しい ClaimsPrincipal で更新されると、フックは元の Authentication コンポーネントをスキップし、元の Authentication コンポーネントの直後にリクエストデリゲートを呼び出します。
認可フック
パイプラインの Authorization コンポーネントの開始前に挿入するフックは、Authentication フックと同様に開始されます。クライアントからのリクエストが統合された DAST からのものでもあることを確認し、そうでない場合はリクエストを元の Authorization コンポーネントに渡します。
フックが統合された DAST ツールからのリクエストであると判断した場合、通常は認可が成功したときに行われるリクエストのコンテキストに対して軽微な更新をします。次に、元の Authorization コンポーネントをスキップし、元の Authorization コンポーネントの直後に要求デリゲートを呼び出す