OCI WAFポリシーでブロックしたリクエストを通知する

# はじめに
以下記事でOCI WAFポリシーを利用してセキュリティ強化を試してみた。
今回は、WAFポリシーでブロックした場合に、運用管理者への通知(メール等)するような仕組みをOCIサービスを利用して簡単に構築してみたいと思う。
- [OCI WAFポリシーでセキュリティ強化してみる](https://qiita.com/ramtan0522/items/aa4f55d5b13655c6c024)

# ゴール
- WAFポリシーでhttpレスポンスコード200以外だった場合にメールで通知する。
- OCIサービスを利用する

# 構成イメージ
[【Oracle Cloud Infrastructureドキュメント 】コネクタ・ハブの概要](https://docs.oracle.com/ja-jp/iaas/Content/connector-hub/overview.htm#flow)を参考とし、**WAFのログ → コネクタ・ハブ → 通知 → メール送信**の流れで構成する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/3d8eec08-b2af-693f-933b-bba31912193f.png)



# 前提条件
OCI上の構成については以下記事で紹介したものとする。
- [OCI 2台のバックエンドサーバにロードバランシングさせてみる（ネットワーク・セキュリティ・グループ利用）](https://qiita.com/ramtan0522/items/a094093a2dcdf8b3bfa9)

WAFポリシーについては[OCI WAFポリシーでセキュリティ強化してみる](https://qiita.com/ramtan0522/items/aa4f55d5b13655c6c024)で記載している内容のうち、以下を設定する。
- 「レート制限」：リクエスト制限：3、期間(秒)：5 でルール適用
- 「保護」：XSSからの保護ルール適用



# 手順


### Loggingの設定
今回、WAFポリシーのログを利用するため、WAFポリシーのログを有効化する

「ナビゲーション・メニュー」→「アイデンティティとセキュリティ」→「Web アプリケーション・ファイアウォール」の下の「ポリシー」をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/e9368e25-44c6-1729-d0b2-b3ceaec3c1f0.png)

ポリシーのリストから作成済のポリシーをクリックし、左下にある「ポリシー」の「ファイアウォール」をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/15078ba6-db17-de43-22bd-b777de9572a7.png)


ファイアウォールのリストから該当のファイアウォール名をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/dd6e761f-d577-350a-f4b3-4b9c0409784e.png)

左下の「ログ」をクリックすると、ログの有効化を選択できるようになるので、有効に変更すれば設定完了。
※ログ名はコネクタ・ハブで利用するので覚えておいていただきたい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/6ca3177d-4fac-516f-2f7c-fae61f33fd81.png)







### Notifications の設定
ここでは、通知したいメールアドレスを登録する設定を行う。
「ナビゲーション・メニュー」→「開発者サービス」→「アプリケーション統合」の下の「通知」をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/92d4b280-4a99-e24c-ad47-862ad2961b61.png)

「トピックの作成」をクリックする。任意の名前を入力する（ここではwaf-mailとした）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/a6634756-0dc6-18c1-4414-58762eef519e.png)

名前 (waf-mail) をクリックしてトピックの詳細に遷移し、「サブスクリプションの作成」をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/fc36a684-4d8e-0baf-8d22-ac20b44602c3.png)
以下入力し、「作成」をクリックする。
- プロトコル：電子メール
- 電子メール：通知したいメールアドレス

状態が、Pending ステータスとなる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/35084ef1-97d6-fe1e-2555-dad14d5369ab.png)

入力したアドレス宛に、サブスクリプション有効化のメールが届くのでConfirm Subscription をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/531b7cdf-8fa9-a8c4-bae6-0b8dae8a80f8.png)
Oracle Cloud Infrastructureのブラウザ画面開く。Subscription confiredと表示される
（キャプチャ取り損ねた）

コンソール画面にて、サブスクリプション状態が Active に変われば、設定完了。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/c1541bc4-e334-d65a-692b-4ba1c39d130d.png)






### Service Connector Hubの設定
出力されたWAFのログからhttpレスポンスコード200以外だった場合に、Notificationsに連携できるようにする。

「ナビゲーション・メニュー」→「アナリティクスとAI」→「メッセージング」の下の「コネクタ・ハブ」をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/87b10949-0696-cc2c-1bfc-a9743f954806.png)

「サービス・コネクタの作成」をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/5413d7b8-37c2-f761-a28c-0c5c68dbbc6a.png)
- ソース：「ロギング」
- ターゲット：「通知」
- ロググループ／ログ：設定したWAFログ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/8e0a9d20-c76c-db83-b0c4-d27f4b616979.png)
- ログ・フィルタ・タスク：以下の通り
    - プロパティ：data.response.code
    - 演算子：!=
    - 値：200
- ファンクション・タスクの構成：特に設定なし
- ターゲット接続の構成：以下の通り
- トピック：設定したwaf-mail
- メッセージ書式：「フォーマットされたメッセージの送信」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/b9cd43c1-23b8-4dd2-c114-ce30056e238b.png)

コネクタ・ハブを利用するにあたり、ポリシーが必要になってくる。以下のような表示が出てきた場合は、「create」をクリックしてポリシーを作成する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/1fcdbc19-fd4f-a5b1-26ab-ca732088dbc4.png)

##### ＜参考＞ポリシーについて
ポリシー作成すると以下のような内容で作成される。
```ruby:ポリシー
allow any-user to use ons-topics in compartment id '[compartment_OCID]' where all {request.principal.type= 'serviceconnector', request.principal.compartment.id='[compartment_OCID]'}
```

※仮にポリシーを当て忘れるとメール通知が失敗して以下のようなエラーメッセージが**サービス・コネクタ (メール通知) 側のログ**に出ていた。後述するが、ログ確認するためには、別途ログの有効化が必要。
```ruby:エラーメッセージ
Run failed due to Destination error, Error Code : 404 NotAuthorizedOrNotFound. For troubleshooting tips, see https://docs.oracle.com/iaas/Content/service-connector-hub/troubleshooting.htm
```



# 動作確認
ブラウザでそれぞれ動作確認を行う。
【動作確認①】
以下URLのように入力し、Web ブラウザ・ページを5 秒で3 回 (以上) リフレッシュする。
~~~bash:URL①
http://[Public IP of FLB]
~~~


【動作確認②】
悪意のあるリクエストを模擬して、以下URLのように入力する。
~~~bash:URL②
http://[Public IP of FLB]/index.html?<pstyle="background:url(javascript:alert(1))">
~~~

「ナビゲーション・メニュー」→「監視および管理」→「ロギング」の下の「ログ」をクリックし、該当のWAFのログを表示する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/1d9396fc-a7f4-0184-4e9b-5514cb2942c6.png)
URL①については3回実行しており、1・2回目はallowが返却された(下から1つ目と2つ目)。
3回目については「Too many requests are being sent to Web Server.」がブラウザ上に表示されて、ログ(下から3つ目)も"**block**"となっていた。

URL②については、「Service Unavailable: Web Server is secured against XSS attacks.」とブラウザ画面に表示されて、ログ（一番上）についても"**block**"となっていた。

それぞれのログについて詳細確認したい方は以下を参照。
<details><summary>URL①のallow応答のログ</summary>

~~~json:URL①のallow応答のログ
{
  "datetime": 1708406138466,
  "logContent": {
    "data": {
      "action": "allow",　　　　　※許可されている
      "backendStatusCode": "200",
      "clientAddr": "xxx.xxx.xxx.xxx",
      "countryCode": "jp",
      "host": "xxx.xxx.xxx.xxx",
      "listenerPort": "80",
      "request": {
        "agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) EdgiOS/120.0.2210.141 Version/17.0 Mobile/15E148 Safari/604.1",
        "httpVersion": "HTTP/1.1",
        "id": "b1a81dbef9ae152257fbd45561d3f0aa",
        "method": "GET",
        "path": "/",
        "time": "0.011"
      },
      "requestProtection": {},
      "response": {
        "backendTime": "0.011",
        "code": "200",　　　　　　　　　　　※httpレスポンスコード200で返している(コネクタ・ハブでフィルタリング設定している箇所)
        "contentType": "text/html; charset=UTF-8",
        "size": "264"
      },
      "responseProtection": {},
      "responseProvider": "10.0.0.184:80",
      "timestamp": "2024-02-20T05:15:30Z"
    },
    "id": "e9e75572-ef47-4a1e-a644-b678bb82c9fc-waf-94",
    "oracle": {
      "compartmentid": "ocid1.compartment.oc1..xxxxxxxxxxxxxxxxxxxxxxx",
      "ingestedtime": "2024-02-20T05:15:47.391Z",
      "loggroupid": "ocid1.loggroup.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxx",
      "logid": "ocid1.log.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxx",
      "resourceid": "ocid1.webappfirewall.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxx",
      "tenantid": "ocid1.tenancy.oc1..xxxxxxxxxxxxxxxxxxxxxxx"
    },
    "source": "lb_2024-0209-1655",
    "specversion": "1.0",
    "subject": "",
    "time": "2024-02-20T05:15:38.466Z",
    "type": "com.oraclecloud.loadbalancer.waf"
  },
  "regionId": "ap-tokyo-1"
}
~~~

</details>





<details><summary>URL①のblock応答のログ</summary>

~~~json:URL①のblock応答のログ
{
  "datetime": 1708406138466,
  "logContent": {
    "data": {
      "action": "block",　　　　　※ブロックされている
      "backendStatusCode": "-",
      "clientAddr": "xxx.xxx.xxx.xxx",
      "countryCode": "jp",
      "host": "xxx.xxx.xxx.xxx",
      "listenerPort": "80",
      "request": {
        "agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) EdgiOS/120.0.2210.141 Version/17.0 Mobile/15E148 Safari/604.1",
        "httpVersion": "HTTP/1.1",
        "id": "1d8f4b95f96cfff1b261cda6c6875686",
        "method": "GET",
        "path": "/",
        "time": "0.000"
      },
      "requestProtection": {},
      "requestRateLimiting": {
        "matchedRules": "rate_limit1"　　　　　　　　　　※このルールによってブロックされた
      },
      "response": {
        "code": "429",　　　　　　　　　　　※httpレスポンスコード429で返している(コネクタ・ハブでフィルタリング設定している箇所)
        "size": "125"
      },
      "responseProtection": {},
      "responseProvider": "requestRateLimiting/rate_limit1",
      "timestamp": "2024-02-20T05:15:31Z"
    },
    "id": "e9e75572-ef47-4a1e-a644-b678bb82c9fc-waf-96",
    "oracle": {
      "compartmentid": "ocid1.compartment.oc1..xxxxxxxxxxxxxxxxxxxxxxx",
      "ingestedtime": "2024-02-20T05:15:47.391Z",
      "loggroupid": "ocid1.loggroup.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxx",
      "logid": "ocid1.log.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxx",
      "resourceid": "ocid1.webappfirewall.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxx",
      "tenantid": "ocid1.tenancy.oc1..xxxxxxxxxxxxxxxxxxxxxxx"
    },
    "source": "lb_2024-0209-1655",
    "specversion": "1.0",
    "subject": "",
    "time": "2024-02-20T05:15:38.466Z",
    "type": "com.oraclecloud.loadbalancer.waf"
  },
  "regionId": "ap-tokyo-1"
}
~~~

</details>





<details><summary>URL②のblock応答のログ</summary>

~~~json:URL②block応答のログ
{
  "datetime": 1708406168770,
  "logContent": {
    "data": {
      "action": "block",　　　　　※ブロックされている
      "backendStatusCode": "-",
      "clientAddr": "xxx.xxx.xxx.xxx",
      "countryCode": "jp",
      "host": "xxx.xxx.xxx.xxx",
      "listenerPort": "80",
      "request": {
        "agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_2_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1",
        "httpVersion": "HTTP/1.1",
        "id": "bcabced4131ba614144ae138dde2e2e8",
        "method": "GET",
        "path": "/index.html?%3Cpstyle=%22background:url(javascript:alert(1))%22%3E",
        "time": "0.000"
      },
      "requestProtection": {
        "matchedData": "Matched Data: \\x22 found within ARGS:<pstyle: \\x22background:url(javascript:alert(1))\\x22>;Matched Data: javascript:a found within ARGS:<pstyle: \\x22background:url(javascript:alert(1))\\x22>",
        "matchedIds": "9421100_v002;941210_v002",
        "matchedRules": "xss-action"　　　　　　　　　　　　※このルールによってブロックされた
      },
      "response": {
        "code": "503",　　　　　　　　　　　※httpレスポンスコード503で返している(コネクタ・ハブでフィルタリング設定している箇所)
        "size": "155"
      },
      "responseProtection": {},
      "responseProvider": "requestProtection/xss-action",
      "timestamp": "2024-02-20T05:16:03Z"
    },
    "id": "d57a27bf-5dfd-40d9-a348-2e4953b4c709-waf-181",
    "oracle": {
      "compartmentid": "ocid1.compartment.oc1..xxxxxxxxxxxxxxxxxxxxxxx",
      "ingestedtime": "2024-02-20T05:16:15.840Z",
      "loggroupid": "ocid1.loggroup.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxx",
      "logid": "ocid1.log.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxx",
      "resourceid": "ocid1.webappfirewall.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxx",
      "tenantid": "ocid1.tenancy.oc1..xxxxxxxxxxxxxxxxxxxxxxx"
    },
    "source": "lb_2024-0209-1655",
    "specversion": "1.0",
    "subject": "",
    "time": "2024-02-20T05:16:08.770Z",
    "type": "com.oraclecloud.loadbalancer.waf"
  },
  "regionId": "ap-tokyo-1"
}

~~~

</details>


ブロックしたリクエストについてはメールを飛ばすようにしていたのでメッセージを確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/a06e711b-05ef-723e-9240-400bd6ca0cf8.png)

**ブロックしたリクエストの分、メールが送信されている**ので、今回は2通だったが、仮に100回ブロックした場合には100回メール通知されることになる (実運用を想定すると改善が必要な個所になる)。

メールメッセージのDetails/JSONのところには上記ログに出力された内容がそのまま記載されているので最低限の情報はメールでわかりそう。




# メールが届かない場合
前述しているようにポリシーによる権限不足の可能性がある。以下を参照頂きたい。
[＜参考＞ポリシーについて](#-参考ポリシーについて)

またエラーメッセージを確認したい場合は、サービス・コネクタの実行ログも有効にして確認することをオススメする。

有効化の方法は以下の通り。
「ナビゲーション・メニュー」→「アナリティクスとAI」→「メッセージング」の下の「コネクタ・ハブ」をクリック。
該当のサービス・コネクタを選択し、左下にある「リソース」の「ログ」より、サービス・コネクタの実行ログを有効化できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/fb546715-f255-4e05-1597-d954f3fc746a.png)


# その他
### 【サービス・コネクタ】ログ・フィルタ・タスクについて
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/8e0a9d20-c76c-db83-b0c4-d27f4b616979.png)
今回はWAFポリシーのルールにてブロックした場合は、httpレスポンスコードを200番台以外を付与するように設定している。
そのため、ログ・フィルタ・タスクで設定するプロパティについては、事前にログを確認し、該当のプロパティを探して「**data.response.code**」でフィルタをかけた。
他のプロパティでメール通知（コネクタ・ハブによるデータ転送）を制御したい場合は、事前にログを確認してプロパティ・値の検討すると良いと思う（詳細は前述のログを確認して頂きたい）



### 【サービス・コネクタ】メッセージの書式について
メッセージの書式について、「フォーマットされたメッセージの送信」を選択していたが、他方の「RAWメッセージの送信」を選択すると以下のようなメール本文になった。
例えばメール受信をきっかけに、OCIサービスに限らず、何か次のアクションをツール等の自動化で対応する場合など、フォーマット化されていない方が扱いやすい場合もあるので、メール受信側でどう確認するかによっては「RAWメッセージの送信」を選択する場合もありそう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/6a428ac2-22dc-6629-65fa-4867efac3135.png)

### 【サービス・コネクタ挙動確認】ログ検索プロセスの起動頻度について

まず、サービス・コネクタの実行ログを有効化する(再掲)。
「ナビゲーション・メニュー」→「アナリティクスとAI」→「メッセージング」の下の「コネクタ・ハブ」をクリック。
該当のサービス・コネクタを選択し、左下にある「リソース」の「ログ」より、サービス・コネクタの実行ログを有効化できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/fb546715-f255-4e05-1597-d954f3fc746a.png)

有効化した上で、サービス・コネクタがデータ転送する（メールが送信される）ようなリクエストを立て続けに2回実行した。
その際のイベント・ログ数/分を以下のように表示する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/243f7646-3edb-5060-2476-2005e7357e51.png)

これを見ると、データ転送対象がない場合は対象ログを検索するプロセスの起動頻度は低く、1回/分であるが、データ転送対象があった場合は対象ログを検索するプロセスの起動頻度が上がっていることがわかる(20回/分)。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/6b8e5470-8ed5-b815-4571-de568d3e1869.png)
ログを確認すると、データ転送対象あった場合の次回起動は即起動(1秒程度)しており、その後対象なしが続くと次第に起動間隔が少しずつ広くなっていることがわかる。

結論として、OCI内部のロジック (詳細は不明) でデータ転送対象が溜まらないようにこのような動きをするようになっていると思われる。


### 【サービス・コネクタ挙動確認】データ転送失敗時のリトライ
[＜参考＞ポリシーについて](#-参考ポリシーについて)に記載しているが、ポリシーを設定していない状態でデータ転送しようとすると失敗する。
[【Oracle Cloud Infrastructureドキュメント 】配達の詳細](https://docs.oracle.com/en-us/iaas/Content/connector-hub/overview.htm#delivery)にも記載されているが、データ転送処理が成功するまでリトライする。
実際にポリシーを設定するまで何度もリトライをしていた (5分間隔)。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/0cd29e27-76a9-7046-06c1-090bb0d98dd1.png)



### 【サービス・コネクタ挙動確認】設定変更時のデータ転送について
サービス・コネクタの設定を変更した際に、どのような動きになるのかを確認した。
実施前の想定としては以下3つであった。
1. 検索対象ログの先頭からデータ転送対象を探して、対象があれば通知する動き (ソースの保存期間が過ぎて消えている場合は通知できない)
2. 設定変更直前からデータ転送対象を探して、対象があれば通知する動き (設定変更前に全てメール通知が成功していれば再送件数0になる)
3. その他 

サービス・コネクタの[メッセージの書式](#サービス・コネクタメッセージの書式について)を変更することで、今回検証を行った。

結論、「3. その他」になった。
設定変更前に、私の環境では既に約50件以上のメール通知テストを行っていたが、設定変更すると、4件のメール通知がされた。おそらく直近の4件と思われるが、別日に確認した際は8件が再送されたため、再送ロジックは件数ではなさそう。期間かなと推測するが、結局OCIのロジックは公開されていない。
実際にシステムで導入する場合には要件に応じてチューニング・テストが必要になりそう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588872/e1f4d4a6-74ba-31de-76d1-fe072b5c8304.png)




### 色々と確認してみたが…
今回、WAFのログ → コネクタ・ハブ → 通知 → メールの流れで検証を行ってみたが、細かい課題がいくつか出てきた。
試せてはいないが、オプション設定で回避できるものもあるかもしれないが、WAFのログ → コネクタ・ハブ → **モニタリング** → 通知 → メールのように、モニタリングを入れることで閾値監視ができるので、チューニングのしやすさがありそう。



# まとめ
今回は、WAFポリシーでブロックした場合に、運用管理者への通知(メール等)するような仕組みをOCIサービスを利用して簡単に構築した。
WAFポリシーでhttpレスポンスコード200以外だった場合にメールで通知できたし、OCIサービスを利用して構成することもできた。

ただ、細かい部分の設定・チューニングについてはきちんと確認が必要で、モニタリングサービスも利用することで実運用においてチューニングしやすくなると思う。

<!--memo
# 参考URL





Oracle Cloud で Linux のログ監視を実装する
https://qiita.com/itokazu/items/1df6ec019540b2d763cb


-->