django-structlogのログとJupyter Notbookで、ログ解析・セキュリティー対策をした方法を紹介

2020年7月17日

Python
Django
サーバー
IT
Logging
Security

DjangoなブログのアクションログをJupyter Notookでログ解析してセキュリティー対策をしたお話

どうも、テック備忘LOGのYuki(@tech_bibo_log)です!!

今回はDjangoで作成した当ブログのアクセスログから悪質なURLアタックをしているユーザーがいたので、その対策を取った時の方法を紹介していきたいと思います。


今回のブログの経緯としては、前回の記事でも紹介したように、当ブログに迷惑な営業メールが送られてくるようになったことが事の発端でした。
未公開のお問合せフォームを探し出し、国内国外から営業メールを送りつけてくるわけです。


あまりにも頻繁なためセキュリティー対策を取ることに決めた次第です。


django-structlogのログ取得とJupyter NoteBookを使用したシンプルなログ解析で
Webサーバーにアクセスしては悪さをしているユーザーを特定するという、
セキュリティーには疎い筆者でも出来る簡単で一般的な方法での対策となっています。


それでは、さっそく説明していきます!がその前に...
余談ですが、次の記事では当ブログを作成した時の手順について説明しています。

▽▽▽ Djangoでブログを構築した時のお話し ▽▽▽



また、次の記事では前回のdjango-structlogを導入する方法について説明しています。

▽▽▽ django-structlogでDjangoのログを取る方法を紹介 ▽▽▽



では早速やっていきましょう!!

[ 目次 (開く) ]

django-structlogのログを取得

前回の記事でインストールしたdjango-structlogから出力されたログファイルをローカルのどこかに配置しましょう。
※前回の記事を読んでいない方は、まず前回の記事を参考に読むとより分かりやすいです。

前回の記事では、下記にログが吐かれると説明しました。

4.ログファイルの出力先について
以下の部分で通常のテキストログと、Json形式のログの出力先を指定しています。

"filename": "logs/json.log",
"filename": "logs/flat_line.log",

上記の場合は、<プロジェクトフォルダ>/logs/の下にログファイルが生成されます。
※<アプリフォルダ>ではないのでご注意を

という事でSFTPコマンドやSCPコマンドを使用してファイルを取得し、PCの好きな場所に配置しましょう。
※私はWinSCPというツールを使ってファイルの送受信をしています。

Jupyter NoteBookでログを解析

1.新規プロジェクトを作成しよう!

※Jupyter Notookはインストール済みの前提で進めます。

まず、Jupyter NoteBookを立ち上げたら、好きなディレクトリに移動して新規のプロジェクト(?)を作成しましょう。
[ NEW ] - [ Python3 ]とボタンをクリックすると新しく作成できます。 Jupyter NoteBook


ちなみに、ログファイルはこのボタンでアップロードもできます。 Jupyter NoteBookファイルアップロード


[ Python3 ]を選択する..。 Jupyter NoteBookプロジェクト画面


すると次のような画面が開きます。 Jupyter NoteBookプロジェクト画面


この画面で実際に解析を行っていきます。



2.ログを解析しよう!

それでは早速ログを解析していきましょう!!
解析するときのPythonのプログラムはべたべたでお恥ずかしいコードですが、公開していますのでご自由にご使用ください。

いきなり解析結果ですが、このプログラムで解析出来るのは次の5項目となっています。

↓↓↓ 解析結果 ↓↓↓
■IPアドレス      :
■リクエスト内容    :
■アクセスIPアドレス数 :
■アクセスIPアドレス一覧:
■特定IPアドレス アクセス回数(xxx.xxx.xxx.xxx):
Jupyter NoteBookプロジェクト作成



3.ログ解析プログラムの使い方!

使い方は少し手間がかかりますが、以下の通りです。
プログラムとその説明が記載されています!!

それぞれ解析したい内容や、除外したいIPなどは、アクセスログの内容に沿って自分でセットしてください。
※簡単なプログラムの知識かあれば(もしくはなくても読んで理解できれば)プログラムの改修・改善も可能かと思います。

プログラムを実行すると先に説明した解析結果が取得できます。

import matplotlib.pyplot as plt
import json as json

## ログファイルを指定
f = open('C:\\Users\\xxxxx xxxxx\\Downloads\\MyDocs\\Study\\DataScience\\BlogAccess\\access.json', 'r', encoding="utf-8")

row_no = 0

## ループ
while True:

    ## ファイルを読み取る
    line = f.readline()

    if line:

        ## 1行ずつjson形式のString型ログをjson型に変換
        json_str = json.loads(line)        

        ## 1行ずつ必要な項目のみ抽出し、[ key、value ]の2次元配列に格納(子配列)
        dict["ip"] = json_str["ip"]
        dict["ts"] = json_str["timestamp"]
        dict["id"] = json_str["request_id"]
        dict["req"] = json_str["request"]
        #print(row_no, ": ", dict)


        ## [ key、value ]の2次元配列に格納(親配列)
        dictWrap[row_no] = dict
        row_no=row_no+1

        ## 配列を初期化
        dict = {}

    else:
        print(row_no, ":", line)
        break


## [ https://lab.syncer.jp/Tool/JSON-Viewer/ ]で配列の形式と整合性を確認
json = json.dumps(dictWrap)
#print(json)


bad_ip_list = []
bad_req_list = []


## 自分のスマホやPCのIPを除外したいので、予めここでリストに渡しておく
## IPはここで確認可能 [ https://www.cman.jp/network/support/go_access.cgi ]
myIPs = ["xxx.xxx.xxx.xxx", "xxx.xxx.xxx.xxx"]

## 検索ワードを格納
searchWords = ['php', 'PHP', '?', 'login', 'admin']

## jsonデータの多次元配列を1つずつ回していく
for k, v in dictWrap.items():

    ## 各項目を変数に代入
    ip  = v["ip"]
    ts  = v["ts"]
    id  = v["id"]
    req = v["req"]

    ## reqから各検索ワードリストを含むログを探す
    for word in searchWords:
        if word in req:

            if ip in myIPs:
                #print("自分: " + ip)
                continue
            else:

                ## 悪意あるIPアドレスを重複なく格納
                if ip not in bad_ip_list:
                    bad_ip_list.append(ip)
                    print("【BAD_IP 】: " + ip + "【REQUEST】: " + req)    

                ## 悪意あるリクエストを重複なく格納
                if req not in bad_req_list:
                    bad_req_list.append(req)
                    print("【BAD_REQ】: " + req)


    ## トータルのアクセスIP情報を追加で格納していく
    all_ip_list.append(ip)


    ## 変数を初期化
    ip = ts =  id = req = ""


## set関数でリスト型を集合型に型変換して重複を無くす
ip_list = set(all_ip_list)


print('■特定IPアドレス アクセス回数: \n  ( xxx.xxx.xxx.xxx ) --> {}回'.format(all_ip_list.count('xxx.xxx.xxx.xxx')))
print(f'■アクセスIPアドレス総数: \n  {len(ip_list)} 個')
print(f'■アクセスIPアドレス一覧: \n  {ip_list}')




最後に解析結果から、アクセスを制限したいIPアドレスを精査しておきましょう!!!

Nginxのconfigで特定のIPからのアクセスを制限

それでは最後に精査したIPアドレスをNginxの設定ファイルに記載してアクセス制限をかけてしまいましょう。
※もっといい方法とかあるのかな...?知りたい。笑

/etc/nginx/conf.d/hogehoge.conf
server {
    listen  443 ssl;
    server_name  yuki-yamashita.site;
    ssl_certificate         ******************************************************;
    ssl_certificate_key     ******************************************************;

    ...

    # BLOCKED IPs LIST
    deny xxx.xxx.xxx.xxx;
    deny xxx.xxx.xxx.xxx;
    deny xxx.xxx.xxx.xxx;
    deny xxx.xxx.xxx.xxx;
    deny xxx.xxx.xxx.xxx;

    ...

    }
    

そして、Nginxを再起動。

# systemctl restart nginx
# または、systemctl reload nginx

以上!!

お疲れ様でした。
これで「誰がいつアクセスしたか?」というログから迷惑メール悪意のあるユーザーのアクションに対して対策を取ることが出来ましたね!

まとめ

今回やった作業を簡単にまとめると下記の4つになります。

  1. django-structlogのログをローカルに取得

  2. Jupyter NoteBookでログを解析

  3. Nginxのconfigで特定のIPアクセスを制限

  4. Nginxサーバーを再起動

とても簡単ですね!

以上、ありがとうございました。