Cloudflare WAF で検知したイベントのアラート通知

Cloudflare の WAF が検知したセキュリティイベントのアラート通知（Notification）を試します。

## 試験手順

本家ガイド [Cloudflare Notifications](https://developers.cloudflare.com/fundamentals/notifications/)

1. 通知宛先を設定(オプション)

通知先は Email（全プラン）, PagerDuty（Businessプラン以上）, webhooks（Professionalプラン以上） があります。
Email 以外はあらかじめ通知先を定義します。

![Screenshot 2023-08-22 at 20.00.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/8b3ee80e-c29f-ac24-2c24-fcbd4f02071c.png)

> [tines](https://www.tines.com/) への ｗebhook の[設定例](https://qiita.com/KeioCF/items/43e960ba21661c0b4bf6)

2\. 実際の通知を作成するため、「通知（Notification）」を追加

Cloudflare プロダクトに関する様々な定義済みのアラート通知を有効にすることができます。

![Screenshot 2023-08-22 at 20.03.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/18ab4946-f6b0-47d7-268c-66c26d928a78.png)

3\. WAF アラートの通知タイプを選択

WAF のアラート通知には[ 2 つのタイプ](https://developers.cloudflare.com/waf/reference/alerts/#alert-types)があります。
- セキュリティイベントアラート（Businessプラン以上）
- 高度なセキュリティイベントアラート（Enterpriseプラン）
![Screenshot 2023-08-26 at 14.42.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/0f71ef77-60cd-4d15-2211-4f2c1aa15840.png)

今回は検知から通知までの時間も早く、柔軟な設定が可能な高度な方で進めます。

> Free プランでも API を利用すれば更に細かい条件を自作アラート通知に組み込めます（後述）

通知先は Email と 1 で作成した webhook を選択しています。

![Screenshot 2023-08-22 at 20.22.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/caae0d3d-3aab-04a4-983e-10c8e75a5c6e.png)

4\. アラート通知対象のゾーンとサービスを選択

- ゾーンの選択
![Screenshot 2023-08-26 at 15.16.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/39d4ecb9-583d-981d-cce0-7bb275144517.png)
- サービスの選択

今回は WAF の[カスタムルール](https://developers.cloudflare.com/waf/#custom-rules)が生成したイベントをソースにします。
カスタムルールは firewallCustom になります。
![Screenshot 2023-08-26 at 15.16.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/8e3ef9b7-d951-7c74-e0ec-51e70051f26d.png)

- アラート対象のアクションを絞る場合

イベントに含まれるアクションでアラートを絞りたい場合は選択が可能です。（例えば Block のみ対象にしたい、など）
デフォルトではすべてが対象になります。
![Screenshot 2023-08-26 at 18.54.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/7c6ac88c-68f5-9914-1506-bf2a32826a26.png)

これはダッシュボードでイベントを確認する際に、アクションをキーに抽出するのと同じ感覚ですね。
![Screenshot 2023-08-26 at 18.57.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/c647c16b-7266-120e-332e-a5e27f292b1a.png)

これで通知の作成は終わりです。

5\. メイン画面から送信テスト（オプション）

「テスト」をクリックすると擬似的なアラート通知の送信ができます。
![Screenshot 2023-08-22 at 20.26.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/5b1bd0d6-3fe9-15ce-6b50-ef6f97d440ef.png)

6\. WAF カスタムルールの作成

実際にアラートを発生させるため、ルールを設定します。
日本からの通信をブロックして、WAF のイベントを発生させるためのルールを作っています。
![Screenshot 2023-08-22 at 20.29.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/259d7332-4e75-fb06-f425-c0de741ec8ca.png)

7\. ルールにヒットするリクエストを送信

下記の方針でリクエストを投げました。
- 二つのカスタムルールを用意
- ぞれぞれのルールが生成したイベントを合計し、カスタムルール全体で200を超える程度のリクエストを投げる
> 200を超えさせる理由は後述

![Screenshot 2023-08-26 at 15.54.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/3986ab89-3667-9238-9870-992f8e899653.png)
![Screenshot 2023-08-26 at 15.52.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/b6dca7ca-4038-d615-90c9-0af02f963447.png)

8\. WAF アラート通知発動

およそ5分で Email と webhook が到着しました。

![Screenshot 2023-08-26 at 15.50.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/723ee56a-0056-cb0a-df9d-b02f13e2d209.png)

## アラート通知のロジック（WAF編）

[本家解説](https://developers.cloudflare.com/waf/reference/alerts/#alert-logic)によると、WAF 関連のアラート通知のロジックについては、下記の数字がポイントになります

|数|単位|
|-|-|
|6|時間|
|5|分間のバケツ|
|200|セキュリティイベントの数|
|3.5|z-score|
|2|時間|

これらを使ったロジックは下記のとおりです。

- 過去6時間のセキュリティイベント数を5分間隔でカウントし、最新の値が z-score 3.5 以上 かつ イベント数が 200を超えている
- 同じイベントはその後2時間は発動されない

セキュリティイベントのアラート通知に z-score の採用や 200 のしきい値との併用についての背景や理由については、こちらの [Cloudflare ブログ](https://blog.cloudflare.com/introducing-thresholds-in-security-event-alerting-a-z-score-love-story/) が詳しいです。

### z-score

イベントのスパイクをシミュレーションしてみましょう。

6時間を5分間隔にすると、72の計測ポイントがあります。

> Cloudflare のダッシュボードの WAF イベントでも6時間の範囲にすると、値は5分間分での表示になりますね

アラート通知をトリガーするには、数字を72個並べて、最後の 201 の z-score が 3.5 以上になればよさそうです。(オソラク）

71個の数字を1-120からランダムに並べ、最後の72個めを201にして z-score を取ってみました。
最後の値(201) は 200を超え、かつ、 z-score 3.7 となり 3.5 を超えました。
こういう具合でアラート通知が発動されるんじゃないかと思います。

<details><summary>Rでの計算</summary>

```
> scale(sample)
             [,1]
 [1,]  0.84778699
 [2,]  1.08873698
 [3,]  0.55329256
 [4,]  1.24937030
 [5,] -0.91917958
 [6,] -0.43727961
 [7,] -1.24044623
 [8,] -0.89240736
 [9,]  1.54386473
[10,]  0.33911480
[11,] -0.94595180
[12,]  1.57063695
[13,] -1.32076289
[14,] -1.18690179
[15,] -0.49082405
[16,]  0.41943146
[17,] -0.24987406
[18,] -0.78531847
[19,] -1.34753511
[20,]  0.15170925
[21,] -0.54436849
[22,] -1.02626846
[23,] -0.43727961
[24,] -0.35696294
[25,] -0.67822959
[26,]  0.76747033
[27,] -1.45462399
[28,] -1.56171288
[29,]  0.25879813
[30,]  0.41943146
[31,] -0.91917958
[32,]  1.11550920
[33,] -0.99949624
[34,]  0.58006478
[35,] -0.19632962
[36,] -1.10658512
[37,] -1.53494065
[38,]  0.87455921
[39,]  0.33911480
[40,] -0.65145737
[41,] -0.11601296
[42,]  1.24937030
[43,] -0.19632962
[44,] -0.62468515
[45,]  1.27614252
[46,]  0.28557035
[47,]  0.66038144
[48,] -0.94595180
[49,] -0.57114071
[50,]  1.57063695
[51,]  0.84778699
[52,] -1.37430733
[53,]  0.09816481
[54,] -0.91917958
[55,]  1.46354807
[56,]  0.79424255
[57,]  0.60683700
[58,] -1.45462399
[59,]  0.41943146
[60,] -1.18690179
[61,]  0.17848147
[62,] -0.54436849
[63,] -0.19632962
[64,]  0.28557035
[65,]  0.33911480
[66,]  0.41943146
[67,]  0.87455921
[68,]  0.79424255
[69,]  1.06196476
[70,]  0.60683700
[71,] -0.30341850
[72,]  3.76595905
attr(,"scaled:center")
[1] 59.33333
attr(,"scaled:scale")
[1] 37.35215
>
> plot(scale(sample),type="b")
> plot(sample,type="b")
```
</details>


グラフにすると、これくらいのハネ加減になりました。
横軸が6時間で、過去の5分間隔のイベント数が置かれてるイメージです。

- 縦軸：z-score 
![Screenshot 2023-08-26 at 16.23.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/f60f3bde-0e25-c733-5158-d6eb8724d854.png)


- 縦軸：実数（イベント数見立て）
![Screenshot 2023-08-26 at 16.23.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/98deecfb-abd5-b227-e5a5-e7ecf391cb35.png)


なお、トリガーについては、各アラート通知でそのソースイベントの性質と通知の目的によってロジックが異なります。
あくまで上記は WAF 関連のものになります。
[オリジンエラーレート](https://blog.cloudflare.com/smarter-origin-service-level-monitoring/)の例


## 自前でモニターの例
### Analytics API（Free プランでも使える）

[Analytics API](https://developers.cloudflare.com/analytics/graphql-api/) でもダッシュボードと同じ様な WAF 関連の統計情報がとれます。

色々な視点からデータを分析できますので、自身で好きなトリガーを作ることができそうです。また、こちらは**プランに関係なく**利用できるのが利点です。

- WAF アラート通知よりも細かい条件でソースを作成することができる
（下記の例では、WAF アラートにはついてない、ホスト名での絞り込みを実施）
- **通知の仕組みは別途用意**する必要があります

下記では、直近5分で特定ホストに対してカスタムルールでブロックまたはマネージドチャレンジしたイベントの数、を取ることができます。（macOS で実施）
 
```shell:macOS の date 
dt2=`date -Iseconds` 
dt1=`date -Iseconds -v-5M` 
cred="<TOKEN>" 
ztag="<ZONEID>"
wafsrc="firewallCustom"
action='[\"block\",\"managed_challenge\"]'
dhost="www.example.com"

curl "https://api.cloudflare.com/client/v4/graphql" -H "Authorization: Bearer $cred" -H "Content-Type: application/json" -d '{"query":"{viewer{zones(filter:{zoneTag:\"'$ztag'\"}){firewallEventsAdaptiveByTimeGroups(filter:{source:\"'$wafsrc'\",datetime_geq:\"'$dt1'\",datetime_lt:\"'$dt2'\",clientRequestHTTPHost:\"'$dhost'\",action_in:'$action'},limit:1,orderBy:[datetimeFiveMinutes_DESC]){count,dimensions{datetimeFiveMinutes}}}}}"}'
```

1分の場合（Linux で実施）
```shell:ubuntu の date
dt2=`date -Iseconds`
dt1=`date -Iseconds -d '1 minutes ago'`
cred="<TOKEN>" 
ztag="<ZONEID>"
wafsrc="firewallCustom"

curl "https://api.cloudflare.com/client/v4/graphql" -H "Authorization: Bearer $cred" -H "Content-Type: application/json" -d '{"query":"{viewer{zones(filter:{zoneTag:\"'$ztag'\"}){firewallEventsAdaptiveByTimeGroups(filter:{source:\"'$wafsrc'\",datetime_geq:\"'$dt1'\",datetime_lt:\"'$dt2'\"},limit:1,orderBy:[datetimeMinute_DESC]){count,dimensions{datetimeMinute}}}}}"}'
```
```:出力例
{
  "data": {
    "viewer": {
      "zones": [
        {
          "firewallEventsAdaptiveByTimeGroups": [
            {
              "count": 98,
              "dimensions": {
                "datetimeMinute": "2023-08-26T08:32:00Z"
              }
            }
          ]
        }
      ]
    }
  },
  "errors": null
}
```

### ログ

firewallEventsAdaptive や fiwall Event ログは [Adaptive sampling](https://developers.cloudflare.com/analytics/graphql-api/sampling/#adaptive-sampling) を採用し、イベント数の増加に応じてサンプリングを利用します。一方、[Logpush](https://developers.cloudflare.com/logs/about/)（Enterpriseプラン）では [HTTP リクエストログ](https://developers.cloudflare.com/analytics/graphql-api/sampling/#access-to-raw-data) は全リクエストをとれるので、そこからロジックを組み立てることもできそうです。

Logpush 自体は [new relic](https://newrelic.com/jp/blog/how-to-relic/on-cloudflare-to-logs-integration)、[Sumo Logic](https://dev.classmethod.jp/articles/cloudflare-http-request-logs-sumologic-logpush/)、[Splunk](https://qiita.com/KeioCF/items/fcf9513f060d2c4289cc) など検索すると SEIM との連携も出てきます。[Bigquery](https://dev.classmethod.jp/articles/analyze-and-visualize-cloudflare-dns-logs-with-bigquery-text-search-capabilities/) での分析など [他にも](https://developers.cloudflare.com/logs/get-started/enable-destinations/) 外部とのつなぎで独自のエコシステムが作れます。


## その他リンク
[Cloudflare のイロイロなプロダクトの通知にタグ付けされている Blog](https://blog.cloudflare.com/tag/notifications/)
Free プランでの[API 利用例](https://qiita.com/KeioCF/items/2814da7b521951f34c5b)
[GraphQL データセットの一覧の取り方](https://qiita.com/khayama/items/bbfb7f19eedfdd757a38)

## 執筆 BGM

夏も終わりだ。。 🍺
Maysa Leak - [Dangerous Summer Night](https://www.youtube.com/watch?v=99mntYFfN24)