サイト攻撃されてたけど、対策しててよかった話

# 今朝起きたらブラックリスト追加通知がきてた

(-ω-).。ｽﾔｧ

Σ(oωo) {ﾋﾟﾛﾝｯ}

(-ω-) {　なんや・・・普段なることがないラインに通知がきとる・・・　）

(’ω’)！！ {　「ブラックリストにIP:XXX.XXX.XX.XXが追加されました」やと！？　）

## 急いでアクセスログテーブルを確認すると・・・

![スクリーンショット 2021-07-23 14.45.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1694582/d3819189-c575-6e33-5075-e231aed745e0.png)
![スクリーンショット 2021-07-23 14.46.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1694582/a7f1caac-aa10-8fb0-1cf3-8439b6f0a690.png)

(-ω-) {これは・・・たぶん管理画面のログイン画面のURLを探してるんやろなぁ・・・）

![スクリーンショット 2021-07-23 15.07.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1694582/f62edb44-3175-f2ca-214a-abd88db93cb5.png)

(＾ω＾) {しっかりとブラックリストに登録されてる！！対策しててよかったで！）


## 攻撃されたサイトについて
・公開して１ヶ月ほど
・Lightsailのlinux2にDjango・nginxのっけたブログみたいなサイト
・google検索では到達できないほど、少ないなコンテンツ量
→どっからきたんや？（ドメイン名がメジャーなワードで構成してるから、ドメインからランダムに？）

## 対策内容
・MIDDLEWAREの最初で、直近60秒以内に35回アクセスで404ページ。45回アクセスでブラックリスト。
※但し、googlebotはホワイトリストに登録する
・ブラックリスト、ホワイトリスト登録で、ライン通知する

・ライントークンはここで発行
https://notify-bot.line.me/ja/

```python:settings.py
# 一番最初になるように設定。
MIDDLEWARE = [
    'base.middle.middle.AppMiddle',
    'django.middleware.security.SecurityMiddleware',
    ....

# ライントークン
LINE_NOTIFY_TOKEN　= 'XXXXXXXXXX'

```

今回メインとなる処理
※ブラックリスト期間7日じゃ短いので365日に修正してます（７日じゃ許せねぇよなぁ？）

```python:base.middle.middle.py
import subprocess
import time

from django.conf import settings
from django.core.cache import cache
from django.http import Http404
from django.utils.deprecation import MiddlewareMixin

from base.service.common.commonService import AccessLogService
from base.utils import lineUtils
from base.utils.requestUtil import RequestUtil

WHITE_IP_LIST = 'white_ip_list'
BLACK_IP_LIST = 'black_ip_list'
CONTROL_IP_LIST = 'control_ip_list'
CONTROL_IP_LIST_DEFAULT = {
    WHITE_IP_LIST: [],
    BLACK_IP_LIST: []
}

class AppMiddle(MiddlewareMixin):

    @staticmethod
    def process_request(request):

        # アクセスログ登録
        if not settings.DEBUG: AccessLogService.insert_access_log(request)
        else: return

        request_util = RequestUtil(request)
        ip = request_util.get_ip()
        control_ip_list = cache.get(CONTROL_IP_LIST, CONTROL_IP_LIST_DEFAULT)
        # ホワイトリスト
        if ip in control_ip_list[WHITE_IP_LIST]: return
        # ブラックリスト
        if ip in control_ip_list[BLACK_IP_LIST]:
            # ブラックリスト対象のIPで期限を確認
            if cache.get('black_ip_' + ip) is None:
                # 期限切れならブラックリストから除外
                control_ip_list[BLACK_IP_LIST].remove(ip)
                cache.set(CONTROL_IP_LIST, control_ip_list)
            else:
                raise Http404("not found")

        ip_time_list = cache.get(ip, [])
        time_temp = time.time()
        # 60秒前の更新記録を削除
        while ip_time_list and (time_temp - ip_time_list[-1]) > 60:
            ip_time_list.pop()
        ip_time_list.insert(0, time_temp)
        cache.set(ip, ip_time_list, timeout=60)
        if len(ip_time_list) > 45:
            control_ip_list[BLACK_IP_LIST].append(ip)
            # 該当IPで365日使用不可とする
            cache.set(CONTROL_IP_LIST, control_ip_list, timeout=60 * 60 * 24 * 365)
            cache.set('black_ip_' + ip, '', timeout=60 * 60 * 24 * 365)
            lineUtils.send_line_notify(f'ブラックリストに登録しました IP：{ip}')
        if len(ip_time_list) > 35:
            if is_google_bot(ip):
                # googlebotのIPをホワイトリストに登録する
                control_ip_list[WHITE_IP_LIST].append(ip)
                cache.set(CONTROL_IP_LIST, control_ip_list, timeout=60 * 60 * 24 * 365)
                lineUtils.send_line_notify(f'ホワイトリストに登録しました IP：{ip}')
            else:
                raise Http404("not found")

# googlebotか判定する
def is_google_bot(ip):
    # googlebotか判定
    try:
        host = subprocess.run(['host', ip], stdout=subprocess.PIPE).stdout.decode().replace('\n', '')
        return host.endswith(('googlebot.com', 'google.com'))
    except:
        return False

```

下記は個人で作ったutilとかserviceとか

```python:base.service.common.commonService.py
"""
    共通サービス
"""
from base.models import ErrLog, AccessLog
from base.utils.requestUtil import RequestUtil

# アクセスログ登録
class AccessLogService:

    @staticmethod
    def insert_access_log(request):

        request_util = RequestUtil(request)

        # アクセスログ登録
        AccessLog.objects.create(
            csrf_token=request_util.get_csrf_cookie(),
            request_url=request_util.get_request_url(),
            request_host_url=request_util.get_request_host_url(),
            time_zone=request_util.get_time_zone(),
            user_agent=request_util.get_user_agent(),
            ip=request_util.get_ip(),
        )

```


```python:base.utils.requestUtil
# HTTP request操作Util
class RequestUtil:

    # request
    request = None

    def __init__(self, request):
        self.request = request

    # CSRF COOKIE取得
    def get_csrf_cookie(self):
        return self.request.META.get('CSRF_COOKIE')

    # リクエストURL取得
    def get_request_url(self):
        return self.request.path

    # リクエストホストURL（送信元）取得
    def get_request_host_url(self):
        return self.request.META.get('HTTP_REFERER')

    # タイムゾーン取得
    def get_time_zone(self):
        return self.request.META.get('TZ')

    # ユーザーエージェント取得
    def get_user_agent(self):
        return self.request.META.get('HTTP_USER_AGENT')

    # アクセスIP取得
    def get_ip(self):
        # 'HTTP_X_FORWARDED_FOR'ヘッダを参照して転送経路のIPアドレスを取得する。
        forwarded_addresses = self.request.META.get('HTTP_X_FORWARDED_FOR')
        if forwarded_addresses:
            # 'HTTP_X_FORWARDED_FOR'ヘッダがある場合: 転送経路の先頭要素を取得する。
            client_addr = forwarded_addresses.split(',')[0]
        else:
            # 'HTTP_X_FORWARDED_FOR'ヘッダがない場合: 直接接続なので'REMOTE_ADDR'ヘッダを参照する。
            client_addr = self.request.META.get('REMOTE_ADDR')

        # 動作確認のため、取得したアドレスをそのまま返す。
        return client_addr

```

```python:base.utils.lineUtils.py
import requests

from django.conf import settings


def send_line_notify(notification_message):
    """
    LINEに通知する
    """
    line_notify_token = settings.LINE_NOTIFY_TOKEN
    line_notify_api = 'https://notify-api.line.me/api/notify'
    headers = {'Authorization': f'Bearer {line_notify_token}'}
    data = {'message': f'message: {notification_message}'}
    requests.post(line_notify_api, headers=headers, data=data)

```

## まとめ
公開して間もないサイトでもサイト攻撃される可能性あるので
対策は十分にしないとやで〜〜〜！

## 参考にしたもの
https://qiita.com/lxk/items/f56a08bc5ec74c7e3883
https://qiita.com/akeome/items/e1e0fecf2e754436afc8