Techfirm Cloud Architect Blog

テックファーム株式会社クラウドインフラグループのブログ

AWS WAFでカウントモードで検知したルールをAmazon Athenaで検索する

はじめに

AWS WAFで新しいルールを設定したり、ルールを変更する際は、設定した内容で想定通り検知できるか検証が必要です。
検証用のウェブACLで行うのが一般的かと思いますが、本番用のウェブACLでも検証を行うことは可能です。
本番用のウェブACLでの検証では、カウントモードを使用します。
ルールのActionをCountにすることで、誤検知してもリクエストをそのまま通すようにできるため、実際のリクエストに影響がないよう対象のルールを検証することが可能です。
ただし、ルール変更の場合は、既存のルールをCountにするとセキュリティ低下のリスクがあるため、新たにカウントモードの検証ルールを追加し、既存のルールは変更しないようにしましょう。

WAFのログ出力の仕様変更

以前のAWS WAFのログの仕様では、カウントモードでルールを検知した場合、ログのActionフィールドに”COUNT”と記録されていました。 ですが、2022年11月のアップデート以降、Actionフィールドには、“ALLOW”・“BLOCK”・“CAPTCHA”のみ記録されるようになり、”COUNT”は記録されなくなりました。
そして、カウントモードで検知されたルールは、Actionフィールドには"ALLOW"と記録され、"COUNT"のアクションは別のフィールドに記録されるようになりました。
本稿では、新仕様以降、"COUNT"されたルールがどのようにログに出力されるかを解説し、Amazon Athenaを使った"COUNT"されたルールの検索方法を説明します。

rulegrouplistフィールド

AWS WAFのログフィールドのうち、ruleGroupListというフィールドには、動作したルールグループの情報がすべて格納されます。
COUNTされたルールやBLOCKされたルール、検知されなかったルールなど、動作したルールすべてのルールの情報がこのフィールドに記録されます。
また、このフィールドはarray型とstruct型とで複雑に構成されています。
AWS WAFのログのテーブル作成DDLの、rulegrouplistの部分は下記の様になっています。

  `rulegrouplist` array <
    struct <
      rulegroupid: string,
      terminatingrule: struct <
        ruleid: string,
        action: string,
        rulematchdetails: array <
          struct <
            conditiontype: string,
            sensitivitylevel: string,
            location: string,
            matcheddata: array < string >
          >
        >
      >,
      nonterminatingmatchingrules: array <
        struct <
          ruleid: string,
          action: string,
          overriddenaction: string,
          rulematchdetails: array <
            struct <
              conditiontype: string,
              sensitivitylevel: string,
              location: string,
              matcheddata: array < string >
            >
          >,
          challengeresponse: struct <
            responsecode: string,
            solvetimestamp: string
          >,
          captcharesponse: struct <
            responsecode: string,
            solvetimestamp: string
          >
        >
      >,
      excludedrules: string
    >
  >,

struct型の中にさらにarray型を格納しているので、かなり複雑な構造をしているように見えますが、実際に格納されたデータを見てみるとそれほど複雑ではありません。
次に実際のデータを提示した上で、その構造について説明します。

rulegrouplistのデータ構造

rulegrouplistフィールドには、下記の様に動作したルールが並列に格納されます。

[
  {
    rulegroupid=ルールグループやルールのIDもしくはARN,
    terminatingrule=リクエストを終了したルール,
    nonterminatingmatchingrules=リクエストに一致する非終了ルール,
    excludedrules=除外されているルールグループ内のルールのリスト
  }
]

rulegrouplistはarray型で、動作したルールが複数ある場合は、上記「{}(波括弧)」の内容が「,(コンマ)」続きで連結されています。
また、COUNTやBLOCK、検査してALLOWとなったルールすべてを並列に格納しており、Actionによってrulegroupidごとの情報の持ち方が異なります。
たとえば、COUNTされたルールは、下記の様にnonterminatingmatchingrulesの中に検知ルールの情報を複数格納します。

[
  {
    rulegroupid=ルールグループやルールのIDもしくはARN,
    terminatingrule=null,
    nonterminatingmatchingrules=
      [
        {
          ruleid=countルール1,
          action=COUNT,
          overriddenaction=ルールアクションのオーバーライド,
          rulematchdetails=リクエストに一致したルールに関する詳細情報
        },
      {
        ruleid=countルール2,
        action=COUNT,
        overriddenaction=ルールアクションのオーバーライド,
        rulematchdetails=リクエストに一致したルールに関する詳細情報
      }
    ],
    excludedrules=除外されているルールグループ内のルールのリスト
  }
]

リスト化すると下記の様になります。

  • rulegroupid(ルールグループやルールのIDもしくはARN)
  • terminatingrule(リクエストを終了したルール)
  • nonterminatingmatchingrules(リクエストに一致する非終了ルール)
    • countルール1
      • action("COUNT")
      • overriddenaction(ルールアクションのオーバーライド)
      • rulematchdetails(リクエストに一致したルールに関する詳細情報)
    • countルール2
      • action("COUNT")
      • overriddenaction(ルールアクションのオーバーライド)
      • rulematchdetails(リクエストに一致したルールに関する詳細情報)

COUNTされたルールをAthenaで検索する

nonterminatingmatchingrulesは、リクエストに一致する非終了ルールです。
リクエストに一致はするものの終了("BLOCK")とはならないルール、つまり"COUNT"ルールと解釈します。
そのため、COUNTされたルールは、rulegrouplistの、nonterminatingmatchingrulesの中のActionが"COUNT"のもの、となります。
以上を踏まえ、COUNTされたルールをAthenaで検索するクエリは下記の様になります。
取得件数が膨大になるので、日付を指定したり、limitで取得件数を指定して実行するとよいでしょう。

SELECT
    t2.ruleid
FROM "waf_logs"
CROSS JOIN UNNEST(rulegrouplist) AS t(t1)
CROSS JOIN UNNEST(t1.nonTerminatingMatchingRules) AS t(t2)
WHERE t2.action='COUNT'
AND DATE_FORMAT(FROM_UNIXTIME(timestamp/1000, 'Asia/Tokyo'), '%Y-%m-%d') = '2024-01-01'

また、検知数の多かったCOUNTルールを取得するクエリは下記の様になります。

SELECT
    count(t2.ruleid) as count,
    t2.ruleid
FROM "waf_logs"
CROSS JOIN UNNEST(rulegrouplist) AS t(t1)
CROSS JOIN UNNEST(t1.nonTerminatingMatchingRules) AS t(t2)
WHERE t2.action='COUNT'
AND DATE_FORMAT(FROM_UNIXTIME(timestamp/1000, 'Asia/Tokyo'), '%Y-%m-%d') = '2024-01-01'
GROUP BY t2.ruleid
ORDER BY count DESC

さいごに

AWS WAFのログの、Actionフィールドのデータの持ち方が変わったことで、COUNTルールを検索する方法がシンプルではなくなりました。
AWS WAFを利用する上でログ集計は非常に重要ですので、本稿の情報が少しでも皆様のお役に立てば幸いです。