
目次
はじめに
こんにちは。 開発本部開発1部トモニテ開発部所属の庄司(@ktanonymous)です。
先日、トモニテで WAF (Web Application Firewall) を導入しました。
WAF の導入により、これまで以上に安心感を持ってサービス運用に向き合えるようになったと感じています。
本記事では、WAF 導入の背景から、実際に調査・検討した内容、そして導入後の運用についてまとめていきます。
WAF 導入の背景
サービス運営をしている中で、攻撃を受けるというのはよくあることだと思います。
トモニテでも攻撃と思われるアクセスを検知することがありますが、直近ではそのようなアクセスが過去に比べても多い状況が続いていました。
以前には、トモニテで SQL インジェクション攻撃を受けたことに関するブログも公開していますので、そちらもご覧ください。
SQL インジェクションのような攻撃に対しては、アプリケーション側のバリデーションの徹底などによる対応をしていましたが、
DoS 攻撃のように大量のリクエストを送りつけることでシステム負荷を高めるような攻撃に対しては、
社内 Slack でアラートが発報され、都度状況確認するという対応をしていました。
システムの過負荷を検知した時にアラートを発報する監視体制が取れているのは良いのですが、平日・休日・昼夜問わず対応を迫られてしまう状況は、対応負荷も高く健全ではないと感じていました。
そこで、大量アクセスによる過負荷が続いたことも踏まえ、WAF の導入を決定しました。
(なお、トモニテではインフラに AWS を利用しているため、AWS WAF を導入しました。)
WAF 導入にあたり調査・検討したこと
WAF を導入するにあたっては、運用・金額コストをできるだけ抑えるミニマム構成を前提として、以下の観点で調査・検討を進めました。
ログの運用
WAF を導入する上で、最小コストで運用できるログは何かを考えました。
結論から述べてしまうと、追加コストなく設定できるサンプルリクエストおよび CloudWatch Metrics を利用することにしました。
S3 にログを保存することも考えましたが、そのためには Kinesis Data Firehose を利用する必要があるため、
シンプルに導入できる構成を採用することにしました。
これらは、Terraform で WAF のルールのリソースを作成する際に以下のように visibility_config というフィールドを定義するだけで設定できます。
visibility_config { cloudwatch_metrics_enabled = true metric_name = "metric_name" sampled_requests_enabled = true }
WAF のサンプルリクエストでは、実際のリクエストの一部がランダムにピックアップされます。 あらゆるリクエストのサンプリングだけでは WAF のルールで検知されたリクエストを確認するのは難しいですが、サンプルリクエストはメトリクス別にフィルタリングして確認することができるので、WAF がどのようなリクエストを検知したのかを十分に確認することができます。

Sampled requests の各項目について
Metric namevisibility_configで設定したmetric_name
Source IP- 該当のルールで検知されたリクエストの送信元 IP アドレス
URI- 該当のルールで検知されたリクエストの URI
Rule Inside rule group- 該当のルールが所属するルールグループ
Action- 該当のルールで検知されたリクエストに対して実行されたアクション
Time- 該当のルールで検知されたリクエストの送信時刻
Slack 通知の実現
WAF で攻撃と思われるリクエストを検知した時および攻撃が止んだ時に Slack へ通知することで
即座に状況を把握できるようにもしたいと考えました。
そこで、追加のコストを掛けずに通知システムを実現するため、以下のような構成を採用しました。

この構成では、WAF のルールの検知状況を CloudWatch Alarm で監視します。
今回の監視対象は、レートベースによる IP アドレスのブロックルールになります。
WAF によってブロックされたということは攻撃と判断されていることになるため、アラームの閾値は 1 件としています。
これにより、攻撃発生時に Slack へ通知されるようになります。
ただし、これだけでは平時のアラームの状態が「データ不足」と認識されてしまうので、
0 件の状態を「正常」であると認識させるために、noBreaching の設定をしておきます1。
この設定を追加しておくことで、攻撃が止みブロックルールの検知数が 0 件に戻った時に、
アラームが「正常」状態に戻り、攻撃が止まったことを Slack へ通知することができるようになります。
(アラームは状態変化によるものなので複数の攻撃を通知することはできませんが、
全ての攻撃が終わったタイミングで「正常」に遷移するため、状況把握という観点では十分だと考えています。)
最終的には、以下のような Terraform コードを実装することで実現しました。
resource "aws_cloudwatch_metric_alarm" "resource_name" { alarm_name = "alarm_name" comparison_operator = "GreaterThanOrEqualToThreshold" evaluation_periods = 1 metric_name = "BlockedRequests" namespace = "AWS/WAFV2" period = 300 statistic = "Sum" threshold = 1 alarm_description = "過剰なリクエストのIPアドレスをブロックしました" alarm_actions = [ var.sns_topic_arn, ] ok_actions = [ var.sns_topic_arn, ] treat_missing_data = "notBreaching" # 0 件の状態を「正常」であると認識させるための設定 dimensions = { WebACL = var.web_acl_name, Rule = var.rule_name, Region = var.region, } }
WAF 導入時の考慮点
WAF を導入する際、攻撃を防ぐために大量リクエストをブロックしたくなりますが、 いきなりブロックするルールを設定してしまうと、ブロックしてはいけないリクエストまでブロックされてしまう可能性があります。 そのため、まずはカウントのルールを設定してから様子を見てからブロックのルールに変更しました。
また、実際にリソースを作成する前に、開発環境で動作確認を行いました。 作成する予定のリソースと同じ構成のものを開発環境に作成し、 簡単なスクリプトで大量リクエストが発生する状況を再現することで、WAF が意図通りに動作するかを確認しました。
おわりに
今回の記事では、トモニテにおける WAF 導入の背景から、調査・検討内容、実際の運用までを紹介しました。
今回の対応のおかげで、安心感を高めることはできたかと思います。 WAF を導入しただけでセキュリティが完璧になるわけではありませんが、 今後もサービスの安全性向上に向けて、アプリケーション・インフラ両面からセキュリティ対策をおろそかにしないようにしていきたいと思います。
今回の記事が、少しでも皆さんのお役に立てれば幸いです。 最後まで読んでいただき、ありがとうございました。