AWS WAF最適化: ルールグループ全体への例外処理をルールグループ内の特定ルールに絞り込んだ例外処理に移行する話
はじめに
AWS WAF を導入する際、注意すべき点として「AWS WAF 導入によって正常なパスが意図せず BLOCK されてないか」が挙げられます。
正常なパスが AWS WAF によって BLOCK されないようにするには、マネージドルールグループに対して正常なパスを判定させないようにするのが一般的です。
例えば /hoge
による正常なリクエストが AWSManagedRulesCommonRuleSet の SizeRestrictions_BODY
ルールによって弾かれてしまうとしましょう。
この場合、スコープダウンステートメントを利用して正常なパスを AWSManagedRulesCommonRuleSet に評価させないことによって SizeRestrictions_BODY
ルールによって弾かれてしまうのを防ぎます。
実際の画面では下記のようにスコープダウンステートメントを設定することで /hoge
を AWSManagedRulesCommonRuleSet 全体の評価対象から外せます。
ただしこの方法には弱点があり、/hoge
で弾かれるのは SizeRestrictions_BODY
ルールだけなのに、AWSManagedRulesCommonRuleSet 全体を評価対象から外してしまっていることによって不必要なセキュリティホールを作ってしまいます。
例えば、/hoge
というパスにおいては、AWSManagedRulesCommonRuleSet に定義されているルールは全て評価しないということになります。
仮に /hoge
というパスが攻撃者にバレていたとしたらそこが WAF の抜け道として狙われるリスクがあります。
弊社では AWS WAF 導入当初、スコープダウンステートメントを使ったマネージドルールグループ全体へ例外条件を適用する実装でしか特定のパスを評価対象から外す方法がないと思っていたので、弱点を承知の上でスコープダウンステートメントを使ったマネージドルールグループ全体へ例外条件を適用する実装をしていました。
しかし、AWS WAF 導入から暫くしてマネージドルールグループの一部のルールに対して例外条件を適用する方法があるということをクラスメソッドさんの下記記事にて知りました。
この方法を知り、スコープダウンステートメントを使ったマネージドルールグループ全体へ例外条件を適用する実装からマネージドルールグループの一部のルールに対して例外条件を適用する実装に移行を行いました。
本記事ではその移行の際に困った点とその解決方法についての解説をします。
もしこれから AWS WAF を導入するというのであれば、クラスメソッドさんの記事を参考に最初から特定パスを特定のルールセット内の特定ルールのみで除外する方法を利用することを強くお勧めします。
スコープダウンステートメント導入から特定ルール例外処理への移行過程
スコープダウンステートメントを使ったマネージドルールグループ全体へ例外条件を適用する実装を導入してから、マネージドルールグループの一部のルールに対して例外条件を適用する実装に移行するまでの間に1年半ほどの期間が空いていました。
スコープダウンステートメントを使ったマネージドルールグループ全体へ例外条件を適用する実装を導入した際に正常なパスがどのルールに検知されたのかの調査記録は残っていますが、実装やアクセスパターンなど諸々変化しているので正常なパスがどのルールに検知されているのか再調査する必要がありました。
AWS WAF の導入前の初期状態(スコープダウンステートメントを使ったマネージドルールグループ全体へ例外条件を適用する前)は、AWSManagedRulesCommonRuleSet のルールを全て COUNT モード扱いで動かしていたので、S3 に保存した waf のログを Athena で検索し、COUNT になっているログのうち正常なパスを洗い出せば問題ありませんでした。
しかし、スコープダウンステートメントを使ったマネージドルールグループ全体へ例外条件を適用する実装を行っている場合、特定パスによるリクエストは AWSManagedRulesCommonRuleSet のルールを完全にスキップしてしまうため、特定パスが今どのルールに検知されているのかに関するログがどこにも残らない仕様となっていました(ルールに検知されていたとしても COUNT 判定すらされないのでログには許可された痕跡しか残らない)。
COUNT ログが残らない件に対する対処方法
まず最初に思いついた対処方法としては、AWSManagedRulesCommonRuleSet を全て COUNT モードに変更し、COUNT 判定されているリクエストを精査し直すことです。
しかし、この方法では一時的に AWS WAF を事実上無効化にすることになってしまします。
ステージング環境で調査が完結するならこの方法でもそこまで問題ありませんが、本番環境のリクエストでどうなるかということをしっかり確かめる必要があったので本番環境で AWS WAF を事実上無効にするのはリスキーであり却下することにしました。
そこで思いついたのが、マネージドルールグループの全てのルールに対して例外条件を適用する実装をする方法です。
スコープダウンステートメントを使ったマネージドルールグループ全体へ例外条件を適用する実装と同じでは?と思うかもしれませんが、この方法ではスコープダウンステートメントを使わないのでそれとは異なります。
具体的には、クラスメソッドさんの記事で紹介されている「マネージドルールグループの一部のルールに対して例外条件を適用する」方法の、「一部のルールに対して」という部分を「全てのルールに対して」に置き換えた内容を実装します。
この方法ならスコープダウンステートメントを使ったマネージドルールグループ全体へ例外条件を適用する実装と同一の挙動を維持しながらも、AWSManagedRulesCommonRuleSet に入ってきた段階で一度 COUNT 判定が行われるため AWS WAF のログに残ります。
最終的に、マネージドルールグループの全てのルールに対して例外条件を適用する実装を行うことによって得られたログを1ヶ月分貯めてから調査することで安全にマネージドルールグループの一部のルールに対して例外条件を適用する実装への移行を行うことができました。
スコープダウンステートメントを使ったマネージドルールグループ全体へ例外条件を適用する実装を導入した際に正常なパスがどのルールに検知されたのかの調査記録は残っていますが、実装やアクセスパターンなど諸々変化しているので正常なパスがどのルールに検知されているのか再調査する必要がありました。
上記で懸念していた通り新たに検知され始めたルールがいくつか確認できたので、マネージドルールグループの全てのルールに対して例外条件を適用する実装を行い改めてログを調査し直せるようにした甲斐がありました。
まとめ
スコープダウンステートメントを使ったマネージドルールグループ全体へ例外条件を適用する実装を行うと、マネージドルールグループの一部のルールに対して例外条件を適用する実装に移行したくなった際、そのままの実装ではどのルールに検知されているのかログから調査する術がなくなってしまいます。
そこで、本記事で紹介したマネージドルールグループの全てのルールに対して例外条件を適用する実装を行うことで既存の挙動を維持しながらどのルールに検知されているのかをログから調査できる状態を作れるという話をしました。
そもそも、スコープダウンステートメントを使ったマネージドルールグループ全体へ例外条件を適用する実装は本当にマネージドルールグループの全てのルールに検知されるパスなどがない限りは使用せず、最初からクラスメソッドさんの記事で紹介されている「マネージドルールグループの一部のルールに対して例外条件を適用する」実装を行うべきです。
ですが、もしマネージドルールグループの一部のルールに対して例外条件を適用する」実装の存在を知らずにスコープダウンステートメントを使ったマネージドルールグループ全体へ例外条件を適用する実装を行ってしまった方がいれば、本記事の内容を参考にマネージドルールグループの一部のルールに対して例外条件を適用する実装への移行にチャレンジしてみてはいかがでしょうか。
Discussion