ちょっと話題の記事

AWS WAFのログをFirehoseでS3に出力しブロックログをS3Selectで確認してみた

AWS WAF 包括ログ出力がサポートされたので、WAFのログをFirehose経由でS3に出力、ログの内容についてS3 Selectで確認してみました。
2018.09.01

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

AWSチームのすずきです。

AWS WAF のアップデートにより、リクエストされたHTTPヘッダーと関連するWAFルール情報が含まれるアクセスログを Amazon Kinesis Data Firehose を利用し S3などに出力する事が可能になりました。

早速、リリースされたAWS WAF の包括的なログ記録機能と、出力されたログをS3 Selectを利用して確認する機会がありましたので、紹介させていただきます。

AWS WAF の包括的なログ記録機能が新たに利用可能に

Firehose設定

AWS WAFのログ出力先となる Amazon Kinesis Data Firehoseと、ログ保管場所として利用するS3バケットの設置を行います。

  • Firehoseのストリーム名は「aws-waf-logs-」で開始する必要があります。

  • Firehoseと同じリージョンにログ出力先として利用するS3バケットを作成しました

  • 今回、データ加工は省略しました。

  • Firehoseの出力先は「Amazon S3」としました。

  • 今回はテストの為、FirehoseのS3出力は最短(60秒)設定としました。
  • S3、Athena、S3 Selectのコストの抑制の為、GZIP圧縮は有効としました。

  • 確認画面

Monitoring

  • 毎秒数百単位のリクエストが発生するWAFのログ出力先として利用する場合、設定実施後、Firehose Streamのメトリックを確認する事をおすすめします。。

  • 東京リージョンのFirehoseの配信ストリームは、1,000 レコード/秒、1,000 トランザクション/秒、1 MiB/秒 の上限が設定されています。これに抵触する可能性がある場合、スロットルによるログ取得漏れを避けるため、AWSサポートへの上限緩和申請を実施してください。

AWS WAF設定

  • ログ記録を有効にするACLを選択、「Logging」タブが追加されました。

  • 先程用意した、「Kinesis Data Firehose」のストリーム名を選択します。

  • HTTPリクエストヘッダのログ出力除外設定も可能です。
  • 認証関連情報などログ記録が望ましくない場合や、ログ出力サイズの削減に利用出来ると思われますが、今回は省略しました。

  • ログ設定後、設定内容が確認出来るようになります。

S3ログ確認

Kinesis Data Firehose経由でS3に出力されたAWSWAFのログ、S3 Selectを利用して確認してみました。

S3 Select

  • AWS WAFのログはJSON形式、今回、FirehoseでGZIP圧縮を有効としたので、その指定を行います。
  • プレビューでWAFログである事を確認後、

BLOCKログ

  • 特定IPアドレスからのアクセスをブロックするルールで遮断したリクエストの抽出を試みました。

  • SQL 式
select * from S3Object s where s."action" = 'BLOCK' limit 1
  • 出力結果
{
    "timestamp": 1535787146518,
    "formatVersion": 1,
    "webaclId": "xxx-xxx-xxx-xxx-xxx",
    "terminatingRuleId": "xxx-xxx-xxx-xxx-xxx",
    "terminatingRuleType": "REGULAR",
    "action": "BLOCK",
    "httpSourceName": "ALB",
    "httpSourceId": "xxx-xxx/xxx-n-xxx-xxx/xxx",
    "ruleGroupList": [
        {
            "ruleGroupId": "xxx-xxx-xxx-xxx-xxx",
            "terminatingRule": null,
            "nonTerminatingMatchingRules": []
        }
    ],
    "rateBasedRuleList": [],
    "nonTerminatingMatchingRules": [],
    "httpRequest": {
        "clientIp": "xxx.xxx.xxx.xxx",
        "country": "JP",
        "headers": [
            {
                "name": "Host",
                "value": "dev.classmethod.jp"
            },
            {
                "name": "Content-Length",
                "value": "0"
            },
            {
                "name": "pragma",
                "value": "no-cache"
            },
            {
                "name": "cache-control",
                "value": "no-cache"
            },
            {
                "name": "user-agent",
                "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.72 Safari/537.36"
            },
            {
                "name": "accept",
                "value": "image/webp,image/apng,image/*,*/*;q=0.8"
            },
            {
                "name": "referer",
                "value": "https://dev.classmethod.jp/"
            },
            {
                "name": "accept-encoding",
                "value": "gzip, deflate, br"
            },
            {
                "name": "accept-language",
                "value": "ja,en-US;q=0.9,en;q=0.8"
            },
            {
                "name": "cookie",
                "value": "_ga=GA1.2.xxxx.xxxx; CookieConsent=-1; visitor_idxxxx=xxxx; (以下略)"
            }
        ],
        "uri": "/favicon.ico",
        "args": "",
        "httpVersion": "HTTP/2.0",
        "httpMethod": "GET",
        "requestId": null
    }
}

ALLOW

  • WAFを通過したリクエストについても確認が可能です。

  • SQL 式

select * from S3Object s where s."action" = 'ALLOW' limit 1
  • 出力結果

{
    "timestamp": 1535787114374,
    "formatVersion": 1,
    "webaclId": "xxx-xxx-xxx-xxx-xxx",
    "terminatingRuleId": "Default_Action",
    "terminatingRuleType": "REGULAR",
    "action": "ALLOW",
    "httpSourceName": "ALB",
    "httpSourceId": "xxx-xxx/xxx-n-xxx-xxx/xxx",
    "ruleGroupList": [
        {
            "ruleGroupId": "xxx-xxx-xxx-xxx-xxx",
            "terminatingRule": null,
            "nonTerminatingMatchingRules": []
        }
    ],
    "rateBasedRuleList": [],
    "nonTerminatingMatchingRules": [],
    "httpRequest": {
        "clientIp": "54.251.31.xxx",
        "country": "SG",
        "headers": [
            {
                "name": "Host",
                "value": "dev.classmethod.jp"
            },
            {
                "name": "User-Agent",
                "value": "Amazon-Route53-Health-Check-Service (ref xxx-xxx-xxx-xxx-xxx; report http://amzn.to/1vsZADi)"
            },
            {
                "name": "Accept",
                "value": "*/*"
            },
            {
                "name": "Accept-Encoding",
                "value": "*"
            }
        ],
        "uri": "/xxx/",
        "args": "",
        "httpVersion": "HTTP/1.1",
        "httpMethod": "GET",
        "requestId": null
    }
}

まとめ

AWS WAF、これまでもサンプルとして提供される情報をLambdaなどを用いて定期的に回収する事で、 ログとしてS3に保存する事は可能でしたが、取得期間、件数などの制限や、設置や維持管理などの課題を伴うものでした。

LambdaでAWS WAFのsampleログを取得してS3に保存する

今回、AWS WAFの包括ログ出力機能がリリースされた事で、 ルールの誤判定などがあった場合でも、原因ルールの特定や回避が実施しやすくなり、 AWS WAF導入の敷居が下がる事が期待できると思われます。

また、これまでELBやCloudFrontのアクセスログでは取得できなかったヘッダー情報、WAFのログから取得出来るようになりました。 LambdaやKinesis Data Analyticsなどを活用した望まれない不正アクセスの検出や、カスタムWAFルールへのニアリアルタイム反映なども、実現できる可能性が高まったと思われます。

AWS WAFのS3へのログ出力は、Amazon Kinesis Data Firehoseの利用費(東京:1GBあたり0.036USD)とS3の費用のみで利用できます。

毎分平均1MB、1時間60MB、1日1.44GB、1ヶ月(30日)43.2GBのログが発生する場合、Firehoseの月額利用費は1.5USD。 S3利用費もFirehoseのGZIP圧縮を利用することでS3ストレージ消費は4GB程度、ライフサイクルで30日保持した場合、0.1USD(4GB×0.025USD)と廉価な利用が可能ですので、ぜひご活用ください。