コンテンツにスキップ

正常カバレッジ

認証を構成できたら、次のステップは、Mayhem for API が API のできるだけ大きい範囲を正常にカバーできるようにすることです。

正常カバレッジとは

Mayhem は、API サーバーからの 2xx 番台のレスポンスを正常とみなし、特定のエンドポイントから 1 回でも正常なレスポンスが返されると、エンドポイントを正常にカバーできたとみなします。

正常レスポンスに関して他の条件がある場合、現時点ではサポートされていませんが、私たちはそのようなユース ケースに関心をもっています。Discord または E-mail support@forallsecure.com でご連絡ください。どのように解決できるかを検討いたします。

正常カバレッジが重要な理由

正常にカバーされていないエンドポイントで問題が見つかっていないことに意味はありません。

Mayhem API testing は、「最も注目すべきテスト結果は、ほとんど正しいリクエストから得られる」というアイデアを基に設計されています。Mayhem が生成するペイロードの一部は完全にランダムです—これも重要なことです—しかし、特定のエンドポイントで正常なペイロードが見つかると、それを開始点として使用し、さらに多くのリクエストを生成します。

正常時にリレーショナル データベースに INSERT ステートメントを実行する単一の POST エンドポイントがあるとします。Mayhem が発行するすべてのリクエストが無効な場合、INSERT ステートメントはまったく実行されません。INSERT ステートメントに SQL インジェクションの脆弱性があっても、見つけることができません。

いっぽう、このエンドポイントに有効なペイロード (INSERT ステートメントが実行される) を渡し、200 が返された場合、正常なペイロードをベースとして使用し、さらに多くのテストを生成します。INSERT 文に SQL インジェクションのバグがある場合、いずれは見つかるでしょう。

これが「正常カバレッジ」を重視する理由です。エンドポイントが正常にカバーされると、問題が見つかっていないことに意味があるという信頼度が大幅に高まります。

正常カバレッジが低い理由

別のセクションで説明されている認証は、正常カバレッジを阻む最初の障壁であることがよくあります。

通常、Mayhem (およびファジング全般) は、「'page' パラメーターは整数でなければならない」といった単純なデータ検証を満たす値を人間の介入なしに問題なく生成することができます。

Mayhem がエンドポイントを正常にカバーできない他の理由には、どんなものがあるでしょうか。

誤った URL

ときには、一番単純な解決方法を思いつくのが難しいこともあります。正常に実行するのが容易なはずのエンドポイントで予期しないエラーが発生する場合、特にエラーが 404 である場合、Mayhem が正しい URL にアクセスしているかどうかを再確認します。

複雑な構造的検証

Info

完全な OpenAPI 仕様を使用してテストを開始した場合、この問題に遭遇する可能性は低下します。

「整数でなければならない」といったランダム値であれば、比較的すばやく生成できるいっぽう、たとえば「'page' という名前のキーが 1 つだけあり、その値が整数である JSON ドキュメントでなければならない」といった条件でランダム値を生成するには、非常に長い時間がかかります。

ステートフルな検証

最も困難な検証は、サービスの現在の状態に基づく検証です。たとえば、「'pages' テーブルの既存の行のユニークな ID でなければならない」といった値を完全にランダムな方法で生成するには、事実上無限の時間がかかる可能性があります。

Mayhem には (あるリクエストの出力の値を別のリクエストの入力値として使用するなど) いくつか役に立つ奥の手がありますが、それでも完全ではありません。

理由が何であっても、正常カバレッジの問題を診断し、修正する際の推奨事項は以下のとおりです。

正常カバレッジの問題の診断

ここを御覧になっているということは、Mayhem が正常にカバーできないエンドポイントが少なくとも 1 つはあるということでしょう。そうであれば、何が起きているかの解明をどのように始めればよいのでしょうか?

テスト対象のサービスを深く理解している (またはそのようなメンバーと密接に連携している) のでないかぎり、この作業は非常に難しい可能性があることは、覚えておく価値があるでしょう。

ステップ 1: フォーカス

エンドポイントを正常にカバーしようとする場合、1 度に 1 つのエンドポイント (または互いに関連するごく少数のエンドポイント) を調査し、ランを比較的短く (60秒またはそれ未満に) することを強く推奨します。

mapi run--include-endpoint <endpoint> フラグを指定するか、こちらで詳細を説明している他の方法ででランをフィルターします。

ステップ 2: ログ

ログを調べるときに探すべきものはパターンです。Mayhem はどのような種類のリクエストを行い、なぜリクエストが失敗しているのでしょうか。Mayhem はどのような種類のリクエストを行っておらず、どうすればそういったリクエストを行わせることができるでしょうか。

自分のサービスであればよく知っているでしょうから、そのようなサービスのログを調査することから始めましょう。

ログだけでは問題を十分に解明できない場合、mapi run--har <file> フラグを指定すると、API テスト全体の完全なリクエスト/レスポンス ペイロードをキャプチャすることもできます。出力は JSON HAR フォーマットです。

ステップ 3: 問い合わせ

遠慮なく Discord または E-mail support@forallsecure.com でお問い合わせください。お客様が持つご自身のサービスに関する知見と、私たちの持つ Mayhem に関する知見があれば、問題を明らかにできるでしょう。

正常カバレッジを改善する方法

URL を修正する

Mayhem は常に各エンドポイントのパスと共通のサーバー URL を連結することで URL を構築します。

パスは API 仕様から直接取得します。

サーバー URL は mapi run への --url <url> 引数によって指定されます。(他の方法もありますが、ここでは、最も明示的なこの方法だけを説明します)

仕様では、すべてのエンドポイントから共通の接頭辞が省略されていることがあります。次の仕様を例とします。

  /version:
    get:
     ...
  /user:
    get:
      ...

完全な URL が https://localhost/api/v2/version および https://localhost/api/v2/user である場合、共通の接頭辞 (/api/v2) を Mayhem に指定するサーバー URL に含める必要があります。

mapi run ... --url 'https://localhost/api/v2'

仕様を改善する

エンドポイントを正常にカバーするために、仕様をより具体的にする必要がある場合がよくあります。

スキーマを指定する

Mayhem が一貫して構造的に有効なペイロードを生成できない場合、通常は、仕様にスキーマを追加するか、スキーマを改善することで解決できます。

OpenAPI では、Schema オブジェクトを使用して、どのようなレベルであっても、必要なだけ詳しく、パラメーターおよびリクエスト ボディのを宣言できます。

たとえば、エンドポイントのリクエスト ボディの必須プロパティをリストするだけで、Mayhem がエンドポイントを正常にカバーできるようになる可能性は大幅に高まります。

  /example:
    post:
      summary: example
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
+             properties:
+               page:
+                 type: integer

一貫した名前と型を使用する

エンドポイント間で受け渡されるステートフルな識別子に同じ名前と型が使用されている場合、Mayhem はより効率的に有効なペイロードをステートフルに生成できます。

たとえば、リソースをリストするエンドポイントがあり、別のエンドポイントが ID によってリソースを取得している場合、識別子の名前と型を同一にすると、Mayhem はより容易に点線を繋げることができます。

  /example:
    get:
      summary: list of examples
      responses:
        '200':
          description: all the examples
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    example_id:
                      type: integer
- /example/{id}:
+ /example/{example_id}:
    get:
      summary: details of a single example
      parameters:
        - in: path
-         name: id
+         name: example_id
          schema:
-           type: string
+           type: integer

Info

Mayhem が API を検証するのに役立つよう仕様に加えた変更は、コード、ドキュメントなど、仕様から派生する他の成果物も改善するので一挙両得です。

仕様でサンプルを指定する

Mayhem がヒントを必要とする状況があります。たとえば、'/user/{username}/settings' のように現在認証されているユーザーのユーザー名を含むエンドポイントがあるとします。

Info

テスト対象のサービスにファジングでは自然に「発見」するのが難しい何らかの事前ロード済みの状態 (認証のためのユーザーなど) が存在する場合、サンプルが必要になることがよくあります。

その場合、OpenAPI では「サンプル」値を使用できます。Mayhem はリクエスト ペイロードを生成する際にサンプルを考慮します。"{username}" がパラメータライズされたエンドポイントでは、サンプルの指定は次のように簡単です。

  /user/{username}/settings:
    get:
      summary: settings
      parameters:
        - in: path
          name: username
          schema:
            type: string
+           example: mayhem4api-user

リソース ヒント

エンドポイントに対して正常なリクエストを行うために、Mayhem がさらにヒントを必要とする場合があります。すべてのリクエストは、リクエスト ボディまたはパス パラメーターに定数値を適用する必要があるかもしれません。mapi run コマンドに 1 つまたは複数の --resource-hint オプションを渡すか、mapi 構成ファイルにオプションを定義することで、生成されるすべてのリクエストに適用されるヒントを指定できます。
参照:

どのリソースが --resource-hint の対象となるかを理解するには、まず、mapi describe specfication コマンドを使用して、API 仕様で利用可能なリソースをリストします。

例として、petstore demo API のリソースをリストしましょう。

mapi describe specification \
  "https://demo-api.mayhem.security/api/v3/openapi.json"

Spec path is https://demo-api.mayhem.security/api/v3/openapi.json
PUT /pet BODY category/id
PUT /pet BODY category/name
PUT /pet BODY id
PUT /pet BODY name
...
PUT /user/{username} BODY userStatus
PUT /user/{username} BODY username
DELETE /user/{username} PATH username

レスポンス内に仕様のすべてリソースがフラット化され、リストされます。たとえば、次のサンプルは、/pet に対して PUT リクエストを生成する際にリクエスト ボディの一部として渡されるフィールド値を示しています。

Method
|
|   Path
|   |
|   |    Request part (can be QUERY, PATH, HEADER or BODY)
v   v    v
PUT /pet BODY category/id
              ^
              Fully qualified path to request body

次のサンプルはパス パラメーターです。

Method
|
|       Path
|       |
|       |               Request part
v       v               v
DELETE /user/{username} PATH username
                             ^
                             Path parameter

mapi run--resource-hint オプションは、コロンで区切られた resource pathvalue のタプルを受け取ります。

たとえば、上記の DELETE メソッドでは、常に同じ usernamefoo を使用したいとします。

mapi run \
     petstore auto "https://demo-api.mayhem.security/api/v3/openapi.json" \
     --url "https://demo-api.mayhem.security/api/v3/" \
     --resource-hint "DELETE /user/\{username\} PATH username:foo"

--resource-hint は、この DELETE リソースに対して生成されるすべてのリクエストが username パラメーターに foo を使用するよう指定しています。

                 Resource Path                          split with ':'
                 v                                      v
--resource-hint "DELETE /user/\{username\} PATH username:foo"
                              ^                          |
                              must escape '{' for        |
                              valid regex                |
                                                         ^ Value to use

--resource-hint では、正規表現によるリソース パスの比較も可能です。たとえば、すべての username 値を foo にするには、次のように指定します。

                         Match ANY request that requires a 'username'
                         v
--resource-hint "username$:foo"

同じリソース パスに一致する --resource-hint が複数指定された場合、Mayhem はリクエストを生成するたびに指定されたヒントの中からランダムに 1 つを選択します。

                Choose between 'foo'  ...       or 'bar'
                v                               v
--resource-hint "username$:foo" --resource-hint "username$:bar"

.mapi 構成ファイル

.mapi 構成ファイルにリソース ヒント グループのコレクションを指定することができます。

#
# Example .mapi configuration file
#
version: "1.0"

...

# You can specify different groups with different combinations of hints
resource_hints:
- name: Group Name A
  hints:
  - "username:foo"
  - "group:bar"
- name: Group Name B
  hints:
  - "username:baz"
  - "group:group-A"
  - "group:group-B" # You can also specify the same regular expression with different values

リソース ヒント グループは、名前フィールドおよびコロンで区切られた resource pathvalue のタプルのコレクションで構成されます。
同じ resource path を複数回指定し、都度異なる value を使用できます。その場合、リソース グループが選択されると、ランダムで値が選択されます。
各タプル (リソース ヒント) には、CLI で指定されたリソース ヒントと同じルールが適用されます。

暗黙的リソース ヒント グループと明示的リソース ヒント グループ

リソース ヒント グループの導入に伴い、値選択アルゴリズムがわずかに変更されました。
リソース ヒントに次の 2 つのカテゴリが存在するようになりました。

  • 暗黙的 (CLI から指定されたリソース ヒント)
  • 明示的 (.mapi 構成ファイルを使用して指定されたリソース ヒント)

CLI から指定されたリソース ヒントは、名前のない単一の暗黙的なグループであるとみなすことができます
リソース ヒント グループの導入に伴う大きな変更点は、mapi がグループからリソース ヒントを選択すると、その後は前回選択したグループから他のすべてのリソースを選択しようとすることです (一致が見つかった場合)。
これによって、リソースに対して重複のない有効な組み合わせを指定することができます

サンプル プログラムを見てみましょう。

次のリソースがあるとします。

PUT /user/{username} BODY userStatus

そして次のリソース ヒント グループがあります。

resource_hints:
- name: Group A
  hints:
  - "username$:foo"
  - "userStatus$:good"

mapi はこのグループのいずれかのリソース ヒント (username$ または userStatus$) を選択するので、次にこのリソースに対するリクエストを生成する際、該当グループの前回選択されなかったリソース ヒントを選択しようとします。こうすることで、mapi はリクエストにとって意味のある値の組み合わせを使用してリクエストを生成できます。

エンドポイントの無視

何らかの理由でエンドポイントを正常にカバーできない場合、欠陥が見つからないという結果の信頼性が低いため、(mapi run--ignore-endpoint <endpoint> 引数を指定して) ファジング中にエンドポイントをスキップすることを考慮しましょう。


ここまでで、Mayhem は相当徹底的に API を検証できており、おそらくあらゆる種類の問題を発見しているでしょう……そして長期的にはより重要なこととして、見つかっていない問題は API に存在していないという信頼を与えてくれます。

次のセクションでは、Mayhem がレポートする問題の特異性と詳細性を最大化するための追加のセットアップ ステップについて説明します。今すぐ次のセクションに移ることも、後回しにすることもできます。