SmartHR Tech Blog

スマートエイチアール開発者ブログ

SmartHRにおけるAWS WAF運用術

こんにちは、セキュリティエンジニアの岩田です。SmartHRではサービスを保護するためAWS WAFを利用しています。今回はSmartHRにおけるAWS WAFの運用事例をご紹介したいと思います。

AWS WAFとは?

AWS WAFはWebアプリケーションの脆弱性への攻撃を検知・ブロックする、Webアプリケーションファイアウォールのサービスです。

現在、AWS WAFには

  • AWS WAF v2
  • AWS WAF Classic

の2種類があります。

SmartHRではv2のリリース以前からAWS WAFを使用しており、まだv2への移行は行っていないため、以下で記載する内容は全てAWS WAF Classicのものになります。AWS WAF v2では該当しない事項もあるかもしれませんので、ご了承ください。

構成

リソースの構成

AWS WAFに関連するリソースの構成と処理の概要は以下の通りです。

f:id:tiwatena:20201117165158p:plain
SmartHR AWS WAFリソース構成

  • AWS WAFはApplication Load Balancer(ALB)に紐づいてWebアプリケーションへのリクエストを検査します。
  • 検知・ブロック時にはCloudWatchのMetric Alarmを使用して、Amazon Simple Notification Service(SNS)およびAWS Chatbot経由でSlackへ通知します。
  • ログはAmazon Kinesis Data Kinesis Firehose経由でS3に保存します。
  • Firehoseのフィルタ機能を使用してブロック時のログを抽出してAmazon Simple Queue Service(SQS)へ追加し、Lambdaでログを分析して必要に応じてブロックIPリストに追加します。

構成はTerraformでコード化して管理しており、別の環境でも同じ構成を簡単に再現できるようにしています。

ルールの構成

WAFのルールは以下のような構成にしています。

  1. IP一致によるブロック
  2. 文字列一致によるブロック
  3. Marketplaceのマネージドルールによるブロック

3でブロックしたログを分析した結果を、1や2に反映していくという形で使用しています。

対応の流れ

次のような流れで対応を行っています。

  1. 通知
  2. ログ確認
  3. 影響確認
  4. 対処
  5. 通報

1. 通知

CloudWatchのMetric Alarmを利用して、Marketplaceのマネージドルールでブロックされた場合にのみSlackに通知を送るようにしています。Chatbotを使うと自分でフォーマットを用意しなくてもグラフィカルな通知を送ってくれます。

f:id:tiwatena:20201117165705p:plain:w400
AWS WAF Metric Alarm通知

IP一致や文字列一致のルールでブロックした場合は、通知を送らないようにしています。これらのルールは、過去に明らかに不正な攻撃だと確認したものを追加していて、改めて確認する必要がないと判断しているためでです。

Metric Alarmではなく、Lambdaでログ内容を含めたより情報がリッチな通知を送る事もできますが、通知はなるべく迅速かつ確実に送るようにしたいとう点から、シンプルな仕組みにしています。

2. ログ確認

通知を受けて、S3に保存された該当時間帯のログを確認します。 まずはS3 Selectを使用してブロックのログを抽出します。

SELECT * FROM S3Object[*] s WHERE s['action'] = 'BLOCK'

さらに、WAFで検出できず許可されてしまっているリクエストも確認するため、ブロックされたリクエストと同一IPからの他のリクエストも抽出しておきます。

SELECT * FROM S3Object[*] s WHERE s['httpRequest']['clientIp'] = 'IPアドレス'

もちろんAthenaを使っても良いのですが、

  • 単純な抽出クエリを実行するだけ
  • ReadOnly権限のユーザーだとAthenaのクエリ実行ができない

という点からS3 Selectを使用しています。

3. 影響確認

ログの内容から、実際に不正な攻撃(True Positive)なのか、正常なリクエストを過検知してしまったもの(False Positive)なのかを判断します。

攻撃の場合

以下のような特徴がある場合は、不正な攻撃の可能性が高いと判断します。

  • パラメータ.httpRequst.args やパス .httpRequest.uri、 ヘッダ .httpRequest.headers に不審な文字列が含まれているもの
  • パス .httpRequest.uri が実際には存在しないもの
  • 前後のリクエストの遷移が正常ではないもの

f:id:tiwatena:20201120135741p:plain
攻撃ログの例

攻撃であると判断した場合は、実際にWebアプリケーションに影響があるものかどうかを次のような方法で調査します。

  • ステージング環境や開発環境で同一のリクエストを送る
  • 特徴的な文字列Googleなどで検索して対象にしている脆弱性を確認する

POSTメソッドのリクエストの場合、Marketplaceのマネージドルールだと .terminatingRuleMatchDetails[] がログに記録されず、何を検知したかが完全にブラックボックスになってしまうため、詳細までは確認できません。ただ、前後のリクエストを見ると少なくとも攻撃なのかそうでないかの判断はできる場合が多いです。

過検知の場合

以下のような特徴がある場合は、正常なリクエストの過検知の可能性が高いと判断します。

  • POSTメソッドで multipart/form-data 形式でファイルをアップロードしているもの
  • POSTメソッドで application/json 形式でのAPI呼び出しのもの
  • 前後のリクエストの遷移が正常なもの

f:id:tiwatena:20201120153752p:plain
過検知ログの例

シンプルな文字列一致のルールで判断しているWAFだと、application/x-www-form-urlencoded 形式以外でのリクエストは過検知してしまう傾向が強いように感じます。例えばアップロードされるファイル内容にSQLっぽい文字列が含まれていると、SQLインジェクションとして検知してしまう事があります。

4. 対処

攻撃の場合

明らかな攻撃を連続して送ってくる送信元IPアドレスは、IP一致によるブロックルールのIPリストに登録して、その後のリクエストを遮断します。

また、IPアドレス以外のリクエストの特徴で判断できるものは、文字列一致によるブロックルールのリストに登録します。ただし、文字列一致のルールに登録する場合は、過検知を防ぐため少なくとも過去1ヶ月分のログを同一条件で検索して、過検知が無いことを確認してから登録しています。

過検知の場合

明らかな過検知の場合や、同一のルールID.ruleGroupList.terminatingRule.ruleIdで繰り返し過検知疑いが発生している場合は、該当のルールIDを除外します。

リクエストのパス単位での除外のような細かい設定はできず、除外はアプリケーション全体で適用されます。このため、ルールID単位で過去の検出傾向を調べてTrue Positive/False Positiveのバランスを確認し、除外するかの判断材料としています。

5. 通報

不正な攻撃の送信元IPアドレスをwhoisで検索して、信頼できるクラウド事業者などが管理しているものだった場合は、Abuse(不正利用)レポートを送ります。

例えば送信元IPがAWS管理のものだった場合は、Webコンソールの以下のページから報告ができます。

https://console.aws.amazon.com/support/contacts?#/report-abuse

自社には影響の無い攻撃だったとしても、他社には影響があるものかもしれません。また、送信元は攻撃者に乗っ取られていて、まだ気づいていないのかもしれません。報告によって適切に対処してもらい、少しでも攻撃の影響を低減したり、攻撃者を追跡する手がかりになってくれればと思っています。

自動IPブロック

上述の運用のうち、ログを分析して明らかな攻撃と判断された送信元IPアドレスを、IP一致によるブロックルールのリストに追加する処理を一部自動化しています。ブロックログの内容があらかじめ定めた条件に一致した場合は、自動的にIPブロックリストに追加してSlackに通知しています。

f:id:tiwatena:20201117170624p:plain:w400
IP自動ブロックSlack通知

「明らかな攻撃」を判定する条件は、Lambdaの中にコードとして記述しています。判定条件はポリシーやルールとして分けて外出しした方が構成としてはきれいだとは思いますが、今のところシンプルな条件しか設定していないので、内部にそのまま記述してしまっています。

コード化によって対応の属人化を防ぎ、知識や経験による判断を明確な形式知として共有する事ができるようになると思っています。

実際の事例

事例1

同一IPから1分間に200リクエストを超える高頻度な検知・ブロックが発生していました。

f:id:tiwatena:20201117170914p:plain:w600
1分毎のブロックリクエスト数

ログを確認すると、存在しないパスに対して様々な脆弱性に対する攻撃コードを送ってスキャンをしているような動きでした。

User-Agentを見ると、確かに脆弱性スキャンツールのようでした。

明らかに攻撃と判断し、送信元IPアドレスをブロックリストに追加しました。

また送信元IPはVPS事業者のものだったため、不正利用レポートを送信しておいたところ、2ヶ月ほどして対処したという次のような返信が来ていました。

Our subscriber sent in the response excerpted below:

-- response excerpt below --

I was running some discovery and analysis of elasticbeanstalk.com and there may have been an error in my script that caused repeated traffic to go to SmartHR, Inc.

-- response excerpt above --

-- Complaint Response Team --

なお、発生時はまだIP自動ブロックの仕組みはありませんでしたが、現在では同様の攻撃が来た場合は自動で送信元IPがブロックリストに追加されます。

事例2

上の事例とは逆に1時間に数回という低頻度での脆弱性スキャンの試みによるブロックが発生していました。 送信元IPをブロックリストに追加しても、数日後には別のIPから同様のスキャンが継続されていました。

f:id:tiwatena:20201117171120p:plain
1時間毎のブロック数 (IPアドレス毎に色分け)

このため、ログからリクエストの特徴を探して、文字列マッチでのブロックで対応しました。

送信元IPは海外のVPNサービスのようでしたが、Webサイトで不正利用レポートの送り先も見つけられなかったので、残念ながら報告は行っていません。

最後に

どんなセキュリティ対策でも同じですが、WAFも導入しておけばそれだけで大丈夫というわけではありません。多層防御の中のセンサーの1つとして、検知時には適切に対処していく事が必要です。ご紹介した内容が、みなさまがWAFを運用していく上で少しでも参考になれば幸いです。

SmartHRでは一緒にセキュリティを強化してくれる仲間を募集中です!

open.talentio.com

open.talentio.com