Google Cloud ArmorのWAF機能の導入

どうも、大曲です。
今回はGCPのWAF機能を調査して導入したのでそれを共有できれば良いなと思います。

調べた項目

  • WAFのルールの模索
    どんなルールがあるのか
  • WAFの結果の可視化
    どんな攻撃があったかを把握できる機能はあるか

Google Cloud Armorとは

https://cloud.google.com/armor/

ネットワークのセキュリティ対策(IP制限など)もありますが
今回はWAFとしての機能を導入しました。

WAFのルールの模索

用語

  • ポリシー:全体設定を指す(デフォルトのルールも決める)
  • ルール:拒否するルール、複数記入して優先度を決める
  • ターゲット:適応させるGCPのリソースを決める(WAFの場合はロードバランサバックエンドサービスであるLBが該当する)

設定項目

ポリシー

f:id:AdwaysEngineerBlog:20210128233402p:plain

項目 説明
ポリシー名 作成後は変更できない
名前 に使用できる文字は、小文字、数字、ハイフンのみ
デフォルトのアクション 許可 or 拒否

WAFの設定ではデフォルトのアクションは許可にしており、
ルールの方で該当するものを拒否していく設定方法をとっています。

ルールの設定項目

f:id:AdwaysEngineerBlog:20210128233428p:plain

項目 説明
条件 基本モード:IP アドレスまたは IP 範囲のみ
詳細モード:CEL(Common Expression Language)を使用して、
IP 範囲、国コード、リクエスト ヘッダー、事前設定済みのルール(クロスサイト スクリプティング(xss)、SQL インジェクション、またはこれらの組み合わせが可能
一致 CELの内容を記述する
アクション 許可 or 拒否
拒否ステータス 403:アクセス拒否
404:ファイル未検出
502:不正なゲートウェイ
アクションを拒否した場合に3つが選択できる
プレビューのみ プレビューモードが作動する(実際のリクエストに影響が出ない)
優先度 優先度は、0(最高)から 2,147,483,647(最低)
0は設定しないことを勧めます※1

※1 理由は躓いた部分で説明

ちなみにルールがエラーの場合は設定更新後に適応処理が実行されますが、そのときにビックリマークが表示されて失敗したことを通知されます。
f:id:AdwaysEngineerBlog:20210128233450p:plain

ルールについて

ルール設定はIPやリクエストパスなどを設定できる他にSQLインジェクションなどの一般的なルールはOSSのOWASP ModSecurity Core Rule Set (CRS)を利用しているようです。

  • SQL インジェクション
  • クロスサイト スクリプティング
  • ローカル・ファイルインクルード(Local file inclusion)
  • リモート・ファイルインクルード(Remote file inclusion)
  • RCE(Remote code execution)

上記はルールセットがありましたが、「SSRF(Server Side Request Forgery)」は無かったです(AWSにはありました)

比較としてAWS WAF ルール一覧はこちら

参考URL:
Google Cloud Armor カスタムルール言語リファレンス
Google Cloud Armor WAF ルールのチューニング

WAFの結果の可視化

メトリクス

Cloud Monitoringに自動的に「Network Security Policies」というダッシュボードが作成されます。

ただし、自動的に作成されるものは基本的にポリシー単位ですのでロードバランサー単位で見たい場合はカスタムメトリクスで作成することをお勧めします。
f:id:AdwaysEngineerBlog:20210128233510p:plain

リクエストがブロック判定されたメトリクスのクエリは以下の通りです。

fetch network_security_policy
| metric 'networksecurity.googleapis.com/https/request_count'
| filter (metric.blocked == true())
| align rate(1m)
| every 1m
| group_by [metric.backend_target_name],
    [value_request_count_aggregate: aggregate(value.request_count)]

リクエスト

Cloud Loggingで1リクエスト単位で確認することができます。
ただし、ログルーターの設定次第では確認出来なくなります。
(例:WAFの拒否ステータスである403にした場合、ログルーター側で403を無視する設定にしているケース)

以下のようなイメージで出力されます。
f:id:AdwaysEngineerBlog:20210128233550p:plain

項目の説明

  • enforcedSecurityPolicy=実際のリクエストの状態
  • previewSecurityPolicy=プレビューモードの状態
項目 説明 備考
priority 適応されたルール優先度 優先度が0の場合は表示されない
name ポリシー名
configureAction セキュリティーポリシーで設定されている内容
許可 or 拒否のアクション
outcome 実際に処理された結果
許可 or 拒否のアクション
preconfigureExprIds 事前に設定されたルールのID(owasp-crs-v030001-id942140-sqli) カスタムルール(origin.region_code == 'AU'..etc)は表示されない

configureActionoutcomeの違いに関して。
configuredActionはセキュリティーポリシーで設定されている内容であり、
outcomeは実際に処理された結果となります。
許可の設定に対して許可された、拒否の設定に対して拒否されたことを示します。 結局、二つは一緒になると思います。

調査で利用したログエクスプローラでのクエリ

  • 拒否されたリクエスト jsonPayload.enforcedSecurityPolicy.outcome="DENY"
  • プレビューモードでの拒否 jsonPayload.previewSecurityPolicy.outcome="DENY"
  • ルールからの抜粋 jsonPayload.previewSecurityPolicy.preconfiguredExprIds=~"owasp-crs-v030001-id942420-sqli"
  • 優先度 jsonPayload.previewSecurityPolicy.priority=2

躓いた部分

ルールの優先度では0を設定したらロギングで表示されない

0にするとCloud LoggingのjsonPayload.previewSecurityPolicyでpriorityが表示されません。 そのため、ルールで設定する優先度は1からにする方が良い。

# ルールの優先度が0の場合はpriorityが欠損する
{
  "jsonPayload": {
    "enforcedSecurityPolicy": {
      "configuredAction": "DENY",
      "outcome": "DENY",
      "name": "test-waf"
    }
  }
}
# ルールの優先度を1にすることでpriorityが欠損しない
{
  "jsonPayload": {
    "enforcedSecurityPolicy": {
      "configuredAction": "DENY",
      "outcome": "DENY",
      "priority": 1,
      "name": "test-waf"
    }
  }
}

モニタリングで利用できる指標でプレビューモードと実際のリクエストの値が一致しない

  • Previewed Requests (Blocked) + Previewed Requests (Allowed) = Previewed Requests (Total)
  • Previewed Requests (Total) = Requests (Total)

上記の二つの式が成り立つと思っていましたが、違いました。

ルールを以下の二つ設定(一つはプレビューモード)した場合。

#1 Deny (403): OWASP ModSecurity Core Rule Set ← プレビューモード
#2 Allow    デフォルトのルール

プレビューモードで許可されたルールは無いため、Previewed Requests (Allowed)は増えません。
あくまでプレビューモードかつ許可するアクションであるルールのみがカウントされる仕様のようです。

それぞれの状態でまとめるとこのようになります。
プレビューモードの有無で数値が区別されているようで想定しいたものとは違うメトリクスでした。

プレビューモード アクション カウントされるメトリクス
あり 許可 Previewed Requests (Allowed)
Previewed Requests (Total)
あり 拒否 Previewed Requests (Blocked)
Previewed Requests (Total)
なし 許可 Requests (Allowed)
Requests (Total)
なし 拒否 Requests (Blocked)
Requests (Total)

まとめ

  • WAFのポリシーの模索
    OWASPのルールを活用すると良い(SQLインジェクション..etc)
    ただし、誤検知のケースもあるため、プレビューを必ず実施した方が良い

  • WAFの結果の可視化
    Cloud Loggingで1リクエスト単位で確認が可能
    Cloud Monitoringのメトリクスでで拒否されたリクエスト数などを把握でき、アラートの設定も可能

導入を通して

  • プレビューモードは非常に優秀
    ルールはガチガチにしたいものの本番への影響がどの程度あるかはわかりません。
    とりあえずプレビューモードで本番で実行して様子見が一番効率的だと思います。
    実際に誤検知していたリクエストもあり、非常に助かりました。

  • ロギングでどのルールが影響したのか把握しやすい
    1リクエストごとで細かくデータを追えるので調査する際は非常に楽でした。

WAFの調査は初めてでしたが、色々と確認できて楽しかったです。