株式会社ヴァンデミックシステム

Blog

<スポンサーリンク>

構成

  • WAFで攻撃を検知したらSlackへ飛ばす

WAF → KinesisFirehose → Lambda → Slack

→ S3バケット

 

WAF

ルール設定

  • ルールは汎用的なものを設定
    • AWSManagedRulesCommonRuleSet
    • AWS-AWSManagedRulesLinuxRuleSet
  • DefaultActionはAllow

 

接続リソース設定

  • 紐づけたいALBを指定

 

Logging and metrics設定

KinesisiFirehoseにログ出力するようにする

 

 

KinesisFirehose

Transform設定

kinesisFirehoseのJson出力をLambdaへ渡す

  • Lambda側でのトリガーでKinesisFirehoseを指定することはできない

 

 

Lambda設定

  • トリガー、送信先は指定なし

 

ソースコード

  • ランタイムはpython3.8
  • AWSManagedRulesCommonRuleSetの中でNoUserAgent_HEADERUserAgent_BadBots_HEADERがやたらでるので、Slackへは飛ばしたくない
  • それ以外のルールはSlackへ飛ばす

 

import base64
import json
import os
import urllib.request

def lambda_handler(event, context):
    output = []

    for record in event['records']:
        output_record = {
            'recordId': record['recordId'],
            'result': 'Ok',
            'data': record['data']
        }
        output.append(output_record)

        a = base64.b64decode(record['data'])
        b = json.loads(a)
        # if b['action'] != 'ALLOW':
        #     print("action not ALLOW")
        if b["ruleGroupList"][0]["terminatingRule"]["ruleId"] != 'NoUserAgent_HEADER' and b["ruleGroupList"][0]["terminatingRule"]["ruleId"] != 'UserAgent_BadBots_HEADER':
            print("terminatingRule is not NoUserAgent_HEADER")
            response = post_slack(b)
            print(output_record)

    return {'records': output}

def post_slack(msg):
    send_data = {
        "username": "notify_slack",
        "icon_emoji": ":vampire:",
        "color":"#D00000",
        "text": "WAF攻撃検知",
        "attachments":[
            {
                "fallback":"fallback Test",
                "color":"#D00000",
                "fields":[
                    {
                        "title":"詳細内容",
                        "value":str(msg)
                    }
                ]
            }
        ]
    }


    send_text = "payload=" + json.dumps(send_data)
    # send_text = "payload=" + json.dumps(jsondata)

    method = 'POST'
    headers = {'Content-Type': 'application/json'}
    WEB_HOOK_URL = os.environ['WEBHOOK_URL']

    request = urllib.request.Request(
        WEB_HOOK_URL,
        data=send_text.encode('utf-8'),
        method=method
    )
    with urllib.request.urlopen(request) as response:
        response_body = response.read().decode('utf-8')
        print("send ok")

 

環境変数

  • SlackのWebhookURLを指定する

 

 

動作確認用のテストデータ

  • kinesisFirehoseから送られるであろうデータ形式
  • dataはkinesisFirehoseからs3バケットに出力されているWAFのログをbase64エンコードしたもの
  • lambda関数では、data部分をデコードし、terminatingRuleを判定するようにしている

 

{
  "invocationId": "invocationIdExample",
  "deliverySteamArn": "arn:aws:kinesis:EXAMPLE",
  "region": "ap-northeast-1",
  "records": [
    {
      "recordId": "49546986683135544286507457936321625675700192471156785154",
      "approximateArrivalTimestamp": 1495072949453,
      "kinesisRecordMetadata": {
        "sequenceNumber": "49545115243490985018280067714973144582180062593244200961",
        "subsequenceNumber": "123456",
        "partitionKey": "partitionKey-03",
        "shardId": "shardId-000000000000",
        "approximateArrivalTimestamp": 1495072949453
      },
      "data": "ew0KCSJ0aW1lc3RhbXAiOiAxNjA0Nzc0MzM1MTEyLA0KCSJmb3JtYXRWZXJzaW9uIjogMSwNCgkid2ViYWNsSWQiOiAiYXJuOmF3czp3YWZ2MjphcC1ub3J0aGVhc3QtMToxMTExMTExMTExMTpyZWdpb25hbC93ZWJhY2wvdmFtZGVtaWMtd2FmdjIveHh4eHh4eHgtY2UwYi00ZWYwLTkxZGUtYjQ0OTg1NmFmNDUxIiwNCgkidGVybWluYXRpbmdSdWxlSWQiOiAiQVdTTWFuYWdlZFJ1bGVzQ29tbW9uUnVsZVNldCIsDQoJInRlcm1pbmF0aW5nUnVsZVR5cGUiOiAiTUFOQUdFRF9SVUxFX0dST1VQIiwNCgkiYWN0aW9uIjogIkJMT0NLIiwNCgkidGVybWluYXRpbmdSdWxlTWF0Y2hEZXRhaWxzIjogW10sDQoJImh0dHBTb3VyY2VOYW1lIjogIkFMQiIsDQoJImh0dHBTb3VyY2VJZCI6ICIxMTExMTExMTExMS1hcHAvdmFtZGVtaWMtZGV2LWJ1c2luZXNzLWRldjItYWxiLzI4YWU3NjcyYzExMTExMWRjIiwNCgkicnVsZUdyb3VwTGlzdCI6IFsNCgkJew0KCQkJInJ1bGVHcm91cElkIjogIkFXUyNBV1NNYW5hZ2VkUnVsZXNDb21tb25SdWxlU2V0IiwNCgkJCSJ0ZXJtaW5hdGluZ1J1bGUiOiB7DQoJCQkJInJ1bGVJZCI6ICJOb1VzZXJBZ2VudF9IRUFERVIiLA0KCQkJCSJhY3Rpb24iOiAiQkxPQ0siLA0KCQkJCSJydWxlTWF0Y2hEZXRhaWxzIjogbnVsbA0KCQkJfSwNCgkJCSJub25UZXJtaW5hdGluZ01hdGNoaW5nUnVsZXMiOiBbXSwNCgkJCSJleGNsdWRlZFJ1bGVzIjogbnVsbA0KCQl9DQoJXSwNCgkicmF0ZUJhc2VkUnVsZUxpc3QiOiBbXSwNCgkibm9uVGVybWluYXRpbmdNYXRjaGluZ1J1bGVzIjogW10sDQoJImh0dHBSZXF1ZXN0Ijogew0KCQkiY2xpZW50SXAiOiAiMTI2LjIwOS4yMjEuNCIsDQoJCSJjb3VudHJ5IjogIkpQIiwNCgkJImhlYWRlcnMiOiBbDQoJCQl7DQoJCQkJIm5hbWUiOiAiSG9zdCIsDQoJCQkJInZhbHVlIjogImRldjIudmFtZGVtaWMuanAiDQoJCQl9LA0KCQkJew0KCQkJCSJuYW1lIjogIkFjY2VwdCIsDQoJCQkJInZhbHVlIjogIiovKiINCgkJCX0NCgkJXSwNCgkJInVyaSI6ICIvIiwNCgkJImFyZ3MiOiAiIiwNCgkJImh0dHBWZXJzaW9uIjogIkhUVFAvMS4xIiwNCgkJImh0dHBNZXRob2QiOiAiR0VUIiwNCgkJInJlcXVlc3RJZCI6ICIxLTVmYTZlOWJmLTIyMmU3NmNkNWJkNDQyYTEyYjg5YmM5YSINCgl9DQp9"
    }
  ]
}

 

テスト

こちらの2パターンのテストを行い、挙動を見る

 

参考

https://dev.classmethod.jp/articles/aws-waf-block-log-pipeline/

 

 

<スポンサーリンク>

コメントを残す

Allowed tags:  you may use these HTML tags and attributes: <a href="">, <strong>, <em>, <h1>, <h2>, <h3>
Please note:  all comments go through moderation.

*

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)