アンチパターン: RegularExpressionProtection ポリシーで「欲張り」な数量子を使用する

現在、ApigeeApigee ハイブリッドのドキュメントを表示しています。
Apigee Edge のドキュメントを表示する

RegularExpressionProtection ポリシーは、実行時に入力パラメータまたはフロー変数に対して評価される正規表現を定義します。このポリシーは通常、コンテンツに対する脅威(SQL インジェクション、JavaScript インジェクションなど)からの保護や、不正な形式のリクエスト パラメータ(メールアドレス、URL など)のチェックに使用されます。

正規表現を定義する対象として、リクエストパス、クエリ パラメータ、フォーム パラメータ、ヘッダー、XML 要素(XPath を使用して定義された XML ペイロード内)、JSON オブジェクト属性(JSONPath を使用して定義された JSON ペイロード内)を設定できます。

次に、SQL インジェクション攻撃からバックエンドを保護する RegularExpressionProtection ポリシーの例を示します。

<!-- /antipatterns/examples/greedy-1.xml -->
<RegularExpressionProtection async="false" continueOnError="false" enabled="true"
  name="RegexProtection">
    <DisplayName>RegexProtection</DisplayName>
    <Properties/>
    <Source>request</Source>
    <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
    <QueryParam name="query">
      <Pattern>[\s]*(?i)((delete)|(exec)|(drop\s*table)|
        (insert)|(shutdown)|(update)|(\bor\b))</Pattern>
    </QueryParam>
</RegularExpressionProtection>

アンチパターン

デフォルトの数量子(*+?)は、「欲張り(greedy)」な性質を持ちます。つまり最初から、できる限り長い文字列を一致させようとします。一致する文字が見つからなければ、徐々にバックトラックを行い、パターン一致を試みます。結果的にパターンに一致する文字列が極端に短い場合は、欲張りな数量子を使用することで、必要以上に長い処理時間がかかる可能性があります。大きなペイロード(数十 KB、数百 KB の規模)であればなおさらです。

次の正規表現の例では、欲張りな演算子である .* が複数のインスタンスで使用されています。

<Pattern>.*Exception in thread.*</Pattern>

この例では、RegularExpressionProtection ポリシーはまず、できる限り長い文字列(文字列全体)との照合を試みます。一致する文字が見つからなければ、徐々にバックトラックを行います。一致文字列がペイロードの先頭または中心に近い位置にある場合は、欲張りな数量子(.* など)を使用することで、「無欲(reluctant)」な数量子(.*? など)やあまり一般的でない「独占的(possessive)」な数量子(.*+ など)を使用する場合に比べ、より多くの処理時間と処理能力を費やす可能性があります。

無欲な数量子(X*?X+?X??など)は、ペイロードの先頭から 1 文字ずつ一致を試み、徐々に文字を追加していきます。独占的な数量子(X?+X*+X++ など)は、ペイロード全体の一致を 1 度だけ試行します。

上述のパターンを、次のサンプル テキストに適用するとします。

Hello this is a sample text with Exception in thread
with lot of text after the Exception text.

この例では、欲張りな .* の使用は効率が悪すぎます。.*Exception in thread.* のパターンが一致するまでのステップ数は 141 となります。代わりに、無欲な数量子を使う .*?Exception in thread.* のパターンを使用すると、ステップ数はわずか 55 で済みます。

影響

RegularExpressionProtection ポリシーでワイルドカード(*)などの欲張りな数量子を使用すると、次の結果が生じる可能性があります。

  • あまり大きくないペイロード サイズ(最大 1 MB)の場合、API リクエスト全体のレイテンシが大きくなる。
  • RegularExpressionProtection ポリシーの実行完了までの時間が長くなる。
  • ペイロードの大きな(1 MB 超の)API リクエストの場合、Apigee ルーターで事前定義したタイムアウト時間が経過すると、504 ゲートウェイ タイムアウト エラーが発生してリクエストが失敗する。
  • 処理量が大きいため、Message Processor の CPU 使用率も高くなり、他の API リクエストにまで影響が及ぶ可能性がある。

ベスト プラクティス

  • RegularExpressionProtection ポリシーで正規表現を使用する場合、.* などの欲張りな数量子の使用を避けます。代わりに、可能な限り、.*? などの無欲な数量子や .*+ などの独占的な数量子(あまり一般的ではない)を使用します。

関連情報