【Python】WAF/Firewallで検知したIPアドレスが危険かどうかをAbuseIPDBでまとめてチェックする

## 概要
WAFやFirewallで日々検知されるログを確認している際に、「このIPアドレスはどこのIPだろう？怪しいIP？」と思うことが多々あると思います。
危険なIPアドレスかどうかを調べる際によく[AbuseIPDB](https://www.abuseipdb.com/)を使用しているのですが、Web上だと1つずつしか確認ができないため、複数のIPアドレスをまとめてチェックしたい時には手間がかかります。
そのため、APIを使用してまとめてチェックするプログラムをPythonで作成しました。

## 前提条件
- AbuseIPDBのアカウントを持っていること
- AbuseIPDBのAPIキーを取得していること
取得方法は[公式ページ](https://www.abuseipdb.com/api.html)を参照してください
- 検知したIPアドレス一覧を「ipaddress-list.csv」に保存すること

## 制限事項
- 無料版だと1日あたり1000IP分までしか検索ができません。

## プログラム
```abuseIp.py
import requests
import json
import csv
import ipaddress

api_key = 'API Key'
max_age_in_days = '90'


def main():
    ip_address_list_file = 'ipaddress-list.csv'
    check_ip_list = read_ip_address_list_file(ip_address_list_file)

    # IPの個数分ドメイン情報を取得する
    # get domain info of ip addresses
    for ip_address in check_ip_list:
        if is_valid_ip(ip_address[0]):
            domain_info = get_domain_info(ip_address[0])
            domain, isp, score, countryCode, is_public = domain_info
            if is_public == True:
                print(f"{ip_address[0]},{domain},{isp},{countryCode},{score}")

            else:
                print(f"{ip_address[0]},Private IP,Private IP,0")
        else:
            print(f"Invalid IP address: {ip_address[0]}")


def get_domain_info(ip_address):
    # This function receives a ip address and check its domain info　by using abuseip api.
    # Then return a list of its domain name, isp info, countryCode, abuse score, and IPAddress info(Public or Private)

    # Defining the api-endpoint
    url = 'https://api.abuseipdb.com/api/v2/check'

    # Curlを投げてドメイン情報を取得
    # get domain info with curl command
    querystring = {
        'ipAddress': ip_address,
        'maxAgeInDays': max_age_in_days
    }

    headers = {
        'Accept': 'application/json',
        'Key': api_key
    }

    response = requests.request(
        method='GET', url=url, headers=headers, params=querystring)

    try:
        # JSONデータを辞書型に変換
        data_dict = response.json()

        # 必要なデータを取得
        domain = data_dict['data']['domain']
        isp = data_dict['data']['isp']
        countryCode = data_dict['data']['countryCode']
        score = data_dict['data']['abuseConfidenceScore']
        is_public = data_dict['data']['isPublic']

        return domain, isp, score, countryCode, is_public

    except KeyError as e:
        print(f"KeyError: {e} in response: {response.text}")
        return None, None, None, None, None


def read_ip_address_list_file(ip_address_list_file):
    # This function reads csv file and return a list of ip addresses

    # 検索したいIPリストを記載したCSVファイルを読み込む
    # read csv file which has ip addresses you want to search domain info
    with open(ip_address_list_file, encoding='utf-8-sig') as f:
        reader = csv.reader(f)
        search_ipaddress = []

        for row in reader:
            # 確認したいIPアドレスをリスト化
            # create a list of ip addresses
            # [['IPアドレス','検知数']・・・]の情報が入る
            # list will be [['IP Address', 'count of ip address']・・・]
            search_ipaddress.append(row)

    return search_ipaddress


def is_valid_ip(ip_addr):
    try:
        ipaddress.ip_address(ip_addr)
        return True
    except ValueError:
        return False


if __name__ == "__main__":
    main()


```

「ipaddress-list.csv」は以下のように準備してください。

```ipaddress-list.csv
192.168.0.1
192.168.0.2
192.168.0.3
・・・
```

結果は以下のようなイメージになります。

```出力イメージ.txt
['XX.XX.XX.XX'],paloaltonetworks.com,Palo Alto Networks Inc,US,100
```

### 2024/1/31追記
IPアドレスのバリデーションや、エラーハンドリングを追加しました。