AWS WAFでほどほどのセキュリティを構築する

# はじめに
## この記事の概要
　今回社内製品のために立てたAPI (非クリティカル) についてセキュリティ対策を行うにあたって、WAFを利用した仕組みを構築してみました。簡単に共有させていただければと思います。
　「ある程度のセキュリティは確保したいけど、あまりガチガチに自動化みたいなところは考えていない」という人の参考になれば、という記事です。
　※. API GatewayやLambdaの方には詳しく触れません。また個々の用語などについても逐一解説しておりません。

## 背景
- API Gateway + Lambdaで、不特定多数のユーザーがアクセスするようなWeb APIを構築
- APIの用途から想定されるアクセス数はかなり少ない
    - 多くても1時間で100リクエストとかそれぐらい (それくらいアクセスがあれば泣いて喜ぶ)
- API自体には認証の仕組みを設けない
    - リクエストに特定のヘッダが付いていれば通す、という程度
- 万が一DDoS攻撃に遭ったりすると嫌なので、様子を見ながらセキュリティ強度を調整したい

## 得られる結果 (ゴール)
- APIは2リクエスト/秒を許可、2リクエスト/秒の状態が続くと秒間3リクエスト目以降が拒否される　(API Gateway)
- 5分以内に同一IPから100を超えるリクエストがあった場合、当該IPからのリクエストをブロック (WAF)
- 上記ブロックが発生した場合、指定したメールアドレスにブロックが発生した旨を通知 (CloudWatch + SNS)
    - ブロックが発生した旨の通知を受け取ったら、必要に応じてブロックされたIPからのリクエストを手動で永続拒否 (WAF)

### 構成図
シンプル過ぎて書いた意味あったかな？と思いつつ一応構成図を載せておきます。
![構成図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2349419/66eb224f-b83a-894a-6d53-f5632e5d8d03.png)


# 手順 (コンソールのUIなどはすべて2024/6/13時点のものです)
## 1. API Gateway
APIがステージにデプロイされていることが前提です。
- 該当のAPIについて、ステージの詳細から「編集」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2349419/fbb4efc0-3a38-e15c-273a-e0187ed627ec.png)
- スロットリングをONにし、レートとバーストに任意の値を設定して「保存」
    - 例ではレートを1、バーストを1としていますが、この時点で「APIは2リクエスト/秒を許可、2リクエスト/秒の状態が続くと秒間3リクエスト目以降が拒否される」が実現されます
    - レートやバーストの意味は[公式](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/stages.html#how-to-stage-settings)にてご確認ください
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2349419/ddb17d78-396a-8766-6912-3307071a9f02.png)


## 2. WAF
- AWS WAFから「Create Web ACL」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2349419/9e26ae87-244e-3b65-8b84-0ccc9d3b832c.png)
### STEP1
- 任意の名称 (Name) とCloudWatchのメトリクス名 (CloudWatch metric name) を入力
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2349419/b6f7cfed-334d-3154-2925-09236773f08c.png)
- 「ADD AWS Resources」をクリック (ポップアップウィンドウが開きます)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2349419/94c374bc-f71c-9b9c-1eef-523068fa6ce0.png)
- (今回はREST APIに制限を掛けたいので) Resource TypeにAmazon API Gateway REST APIを選択
- 対象としたいAPIにチェックを入れ、「Add」をクリック (ポップアップウィンドウが閉じます)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2349419/e0f88bb6-fcb6-3ed9-33b6-7b11d3792f25.png)
- 「Next」をクリックしてSTEP2へ
### STEP2
- 「Add Rules」をクリックし、「Add my own rules and rule groups」を選択 (STEP2がAdd my own rules and rule groupsに切り替わります)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2349419/f799ba97-dcc8-6b7b-b8c5-325cab012565.png)
- Rule typeに「Rule builder」を選択しNameに任意の名前を入力、Typeは「Rate-based rule」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2349419/83c3c059-9030-be1c-344a-d80c8f27dee3.png)
- Rate limitに「100」を入力、Request aggregationに「Source IP address」、Scope of inspection and rate limitingに「Consider all requests」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2349419/ff713dd2-bc1f-a486-2e99-5c4de75fd69e.png)
- 最後のActionでBlockを選択し、「Add rule」をクリック (元のSTEP2へ戻ります)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2349419/1517975a-cd6d-fb9e-c997-55ecbe6f800e.png)
- Default web ACL action for requests that don't match any rulesで「Allow」を選択し、「Next」をクリックしてSTEP3へ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2349419/61c1f44f-f95d-5fb5-ab2d-075c8c962c06.png)

### STEP3～5
- デフォルトのまま「Next」をクリック、最後にSTEP5で「Create Web ACL」をクリックして完了
    - この時点で「5分以内に同一IPから100を超えるリクエストがあった場合、当該IPからのリクエストをブロック」が実現されます

### ログ出力の有効化
- STEP1～5の間でログ出力の設定ができないため、作成されたWeb ACLのNameリンクをクリックして詳細画面を開き「Logging and metrics」タブを表示後「Enable」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2349419/71c52150-28b5-0b77-19bc-6968803dcb0a.png)
- Enable loggingの画面が表示されるので「CloudWatch Logs log group」を選択し、プリフィックスに **aws-waf-logs-** が付いたロググループを選択
    - 適当なロググループがない場合は「Create New」から作成してください
        - 必ずプリフィックスに **aws-waf-logs-** を付けるようにします (付いていないと選択肢としてそもそも表示されません)
- その他デフォルトのまま「Save」をクリック

## 3. CloudWatch ログ
- CloudWatchから「ロググループ」を選択し、前の手順で作成したロググループの名称をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2349419/b314f5ba-4410-7e2f-be71-cde42d667c97.png)
- メトリクスフィルタータブを表示し「メトリクスフィルターを作成」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2349419/549b7ce5-c7b5-2ac7-3c5d-5a151e8309dc.png)

### ステップ1
- フィルターパターンに `{ $.action = "BLOCK" }` を入力し、「Next」をクリック
    - パターンテストは任意でどうぞ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2349419/9b38d4d9-1216-c007-bd92-b2366c5f2e8b.png)

### ステップ 2～3
- 以下を入力し、「Next」をクリック
    - フィルター名：任意の名称
    - メトリクス名前空間：既存の適当な名前空間がなければ作成 (例：/Security/WAF など)
    - メトリクス値：1
    - デフォルト値：0
    - Unit：なし
    - ディメンション：なし
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2349419/76ffd382-c202-161d-61b1-aadd4df7e046.png)
- 最後にステップ3で「メトリクスフィルターを作成」をクリック

## 4. CloudWatch アラーム
以降の手順にはログストリームが生成されている必要があるため、まだログストリームが生成されていないようであればこの時点で一度はAPI Gatewayを叩いておくことをオススメします。

- CloudWatchの「アラーム状態」 (または「すべてのアラーム」) を選択し、「アラームの作成」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2349419/88f8050a-4ba8-2d8a-9bf9-05a0899cf7b7.png)

### ステップ1
- 「メトリクスの選択」をクリック
- CloudWatch ログの手順で作成したメトリクスにチェックを入れ、「メトリクスの選択」をクリック (メトリクスと条件の指定に進みます)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2349419/3363471e-ae79-e34e-6cde-7719e1a0416c.png)
- 統計に「合計」を指定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2349419/13ec3a72-63f1-d001-fc40-6c555881364b.png)
- 条件を次の通り指定し、「次へ」をクリック
    - しきい値の種類：静的
    - {メトリクス名}が次の時... (アラーム条件)：以上
    - ...よりも (しきい値)：1
    - アラームを実行するデータポイント：1/1
    - 欠落データの処理
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2349419/3c282d52-f589-1f91-5703-d1b0d00c9007.png)

### ステップ2
- 以下を選択・入力し「次へ」をクリック
    - アラーム状態トリガー：アラーム状態
    - 次のSNSトピックに通知を送信：任意
        - トピックは必要に応じて新規作成してください
    - 通知以外のアクションについては割愛します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2349419/286a46fd-3a63-269a-73de-dfd1e0d8cc41.png)

### ステップ3～4
- アラーム名を入力、任意で説明を編集して「次へ」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2349419/b931b91e-f9ab-6abb-f79a-a314f2f937b9.png)
- ステップ4で作成内容をプレビューし、問題なければ「アラームの作成」をクリック

# おわりに
　一連の仕組みが出来上がったら、負荷テストをしてみましょう。私は普段使いのPostmanを利用しました。実際に送付されるメールはこんなイメージです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2349419/c5f57b36-a0c5-57fb-7a5c-6d25da41c739.png)
 　少し手順は多いですが、それでもWeb APIのセキュリティ対策が手軽にできるというのはありがたいですね。何かのご参考になれば幸いです。