はじめに
システム管理者やセキュリティアナリストとして業務を行う際、Apache や Nginx といった Web サーバーが生成する膨大なログファイルに遭遇することは珍しくありません。数千行にも及ぶログを手作業で読み解き、悪意のあるアクティビティを見つけ出すことは、非効率的であり、ヒューマンエラーも発生しやすくなります。
この実験では、セキュリティ担当者の役割を担い、Apache Web サーバーのアクセスログを取り込むための Python スクリプトを作成します。Python の正規表現(re)ライブラリを活用することで、IP アドレスを自動的に解析し、既知の SQL インジェクション(SQLi)攻撃のシグネチャを検出します。最後に、これらの調査結果を構造化された CSV レポートとしてエクスポートし、さらなる分析に役立てます。
このモジュールでは、Python が単なるプログラミング言語から、アナリストの能力を何倍にも増幅させるツールへと進化する過程を体験します。ログ分析は、手作業で行うには反復的すぎますが、自動化によって即座に成果を得られる、非常に適したタスクの好例です。
Python によるログファイルの読み込み
このステップでは、Python を使用してログファイルを開き、読み込む方法を学びます。作業ディレクトリには、すでにサンプルとなる Apache ログファイル access.log が用意されています。
ファイルを 1 行ずつ読み込むことは、自動化における基本的なパターンです。手作業でデータをコピーするよりも拡張性が高く、基本的な取り込み手法を変更することなく、フィルタリング、解析、カウント、レポート作成といったロジックを追加する準備が整います。
まずは、サンプルログファイルの内容を確認し、どのようなデータを扱うのかを把握しましょう。ターミナルで cat コマンドを使用してファイルの内容を表示します。
cat access.log
標準的な Apache アクセスログが数行表示されるはずです。これらの行には通常の Web リクエストもあれば、疑わしい SQL インジェクションの試行も含まれています。
次に、このファイルをプログラムで読み込むための Python スクリプトを作成します。nano テキストエディタを使用して log_analyzer.py という名前のファイルを作成しましょう。
nano log_analyzer.py
Python では、open() 関数を使用してファイルを開き、with ステートメントを使用することで、読み込み終了後にファイルが適切に閉じられることを保証します。以下の Python コードをコピーして、nano エディタに貼り付けてください。
def analyze_logs(file_path):
print(f"Opening log file: {file_path}")
## Open the file in read mode ('r')
with open(file_path, 'r') as file:
## Read the file line by line
for line in file:
## Print each line, stripping the extra newline character
print(line.strip())
if __name__ == "__main__":
log_file = "access.log"
analyze_logs(log_file)
nano でファイルを保存するには Ctrl+O を押し、ファイル名を確認して Enter を押します。テストの前にスクリプトの構築を続けたい場合は、nano を開いたまま次のステップに進んでください。ターミナルに戻る準備ができたら Ctrl+X を押します。
作成した Python スクリプトを python3 コマンドで実行します。
python3 log_analyzer.py
先ほど cat コマンドで確認したログ行と全く同じ内容がスクリプトから出力されるはずです。これで、Python スクリプトがログデータを 1 行ずつ正常に取り込めることが確認できました。
正規表現による IP アドレスの抽出
このステップでは、Python スクリプトを強化し、各ログ行から IP アドレスを自動的に抽出できるようにします。これには正規表現(Regex)を使用します。正規表現は、テキスト内のパターンを照合するための強力なツールです。
ログは半構造化データであるため、正規表現が非常に役立ちます。ログは一定のパターンに従っていますが、通常はきれいな Python オブジェクトの形式ではありません。正規表現を使用すると、目的のテキストパターンを記述し、インタプリタに反復的な照合作業を任せることができます。
Python には、正規表現を扱うための組み込みモジュール re が用意されています。このモジュールをインポートし、IPv4 アドレスに一致するパターンを定義する必要があります。IPv4 アドレスは、ドットで区切られた 4 つの数値(0 から 255 の範囲)で構成されています。
log_analyzer.py が nano で開いたままの場合は、そのまま編集を続けてください。閉じてしまった場合は、再度開きます。
nano log_analyzer.py
スクリプトを更新して re モジュールを含め、IP アドレスを検索するロジックを追加します。ファイルの内容をすべて以下のコードに置き換えてください。
import re
def analyze_logs(file_path):
print(f"Analyzing log file: {file_path}\n")
## Define a Regex pattern for an IPv4 address
## \b ensures word boundaries, \d{1,3} matches 1 to 3 digits, \. matches a literal dot
ip_pattern = r'\b(?:\d{1,3}\.){3}\d{1,3}\b'
with open(file_path, 'r') as file:
for line in file:
## re.findall returns a list of all matches in the string
ips_found = re.findall(ip_pattern, line)
if ips_found:
## We take the first IP found in the line
source_ip = ips_found[0]
print(f"Extracted IP: {source_ip}")
if __name__ == "__main__":
log_file = "access.log"
analyze_logs(log_file)
Ctrl+O と Enter でファイルを保存します。この中間バージョンをテストしたい場合は Ctrl+X で終了してスクリプトを実行してください。そうでない場合は、そのまま次のステップに進むことができます。
python3 log_analyzer.py
以下のような出力が表示されるはずです。
Analyzing log file: access.log
Extracted IP: 192.168.1.10
Extracted IP: 10.0.0.15
Extracted IP: 172.16.0.5
Extracted IP: 192.168.1.22
Extracted IP: 10.0.0.25
これで、正規表現の力を使って、ログの他のテキストを無視し、送信元 IP アドレスだけを抽出することに成功しました。
SQLi シグネチャの検出
このステップでは、スクリプトにセキュリティ検出機能を追加します。SQL インジェクション(SQLi)は、悪意のある SQL 文を入力フィールドに挿入する一般的な Web 攻撃手法です。
これにより、スクリプトは単なるパーサーから検出ツールへと進化します。フィールドを抽出するだけでなく、どの行に注意を払うべきかを判断するセキュリティロジックを適用するようになります。収集から解釈へのこの転換こそが、自動化を運用上の価値あるものにします。
攻撃者は、データを盗むために UNION SELECT や、認証を回避するために ' OR '1'='1 といった特定のキーワードをよく使用します。正規表現を使用して、ログ行からこれらの既知の攻撃シグネチャをスキャンできます。
同じファイルを nano で編集している場合は、そのまま作業を続けてください。そうでない場合は、再度開きます。
nano log_analyzer.py
一般的な SQLi フレーズをキャッチするために、新しい正規表現パターンを定義します。(?i) を使用して検索を大文字小文字を区別しないように設定します。これにより、UNION、union、uNiOn のいずれも検出可能になります。スクリプトを以下のコードに更新してください。
import re
def analyze_logs(file_path):
print(f"Scanning log file for SQLi attacks: {file_path}\n")
ip_pattern = r'\b(?:\d{1,3}\.){3}\d{1,3}\b'
## Define a Regex pattern for SQL Injection signatures
## (?i) makes it case-insensitive
## It looks for 'union select' OR 'or 1=1' variations
sqli_pattern = r"(?i)(union.*select|or\s+1\s*=\s*1|'.*or.*')"
with open(file_path, 'r') as file:
for line in file:
ips_found = re.findall(ip_pattern, line)
source_ip = ips_found[0] if ips_found else "Unknown IP"
## re.search returns a match object if the pattern is found, otherwise None
if re.search(sqli_pattern, line):
print(f"[ALERT] SQL Injection detected from IP: {source_ip}")
print(f"Raw Log: {line.strip()}\n")
if __name__ == "__main__":
log_file = "access.log"
analyze_logs(log_file)
Ctrl+O と Enter でファイルを保存します。ターミナルからスクリプトを実行する準備ができたら、Ctrl+X で nano を終了します。
python3 log_analyzer.py
出力には、悪意のあるリクエストが具体的にフラグ立てされるはずです。
Scanning log file for SQLi attacks: access.log
[ALERT] SQL Injection detected from IP: 10.0.0.15
Raw Log: 10.0.0.15 - - [10/Oct/2026:13:55:45 -0700] "GET /login.php?user=admin' OR '1'='1 HTTP/1.1" 200 5120
[ALERT] SQL Injection detected from IP: 192.168.1.22
Raw Log: 192.168.1.22 - - [10/Oct/2026:13:56:12 -0700] "GET /products.php?id=1 UNION SELECT username, password FROM users HTTP/1.1" 200 4012
これで、スクリプトは基本的な侵入検知システム(IDS)として機能し、ログファイル内の疑わしいリクエストを自動的に特定できるようになりました。
CSV レポートへの出力
最後のステップでは、結果を整形し、CSV(カンマ区切り値)ファイルに保存します。ターミナルへの出力はテストには最適ですが、セキュリティチームには、スプレッドシートソフトで開いたり、他のツールに取り込んだりできる構造化されたレポートが必要です。
Python には、CSV ファイルの書き込みを非常に簡単にする組み込みの csv モジュールがあります。
ファイルを開いたままにしている場合は、編集を続けてください。そうでない場合は、再度開きます。
nano log_analyzer.py
csv モジュールをインポートし、report.csv という新しいファイルを書き込みモード('w')で開き、調査結果を書き込みます。コードを以下の最終バージョンに置き換えてください。
import re
import csv
def analyze_logs(file_path, output_csv):
print(f"Analyzing logs and generating report: {output_csv}...")
ip_pattern = r'\b(?:\d{1,3}\.){3}\d{1,3}\b'
sqli_pattern = r"(?i)(union.*select|or\s+1\s*=\s*1|'.*or.*')"
## Open the output CSV file in write mode
with open(output_csv, 'w', newline='') as csv_file:
writer = csv.writer(csv_file)
## Write the header row
writer.writerow(['Source IP', 'SQLi Detected', 'Raw Log'])
with open(file_path, 'r') as file:
for line in file:
clean_line = line.strip()
ips_found = re.findall(ip_pattern, clean_line)
source_ip = ips_found[0] if ips_found else "Unknown IP"
## Check if SQLi is detected
sqli_detected = "Yes" if re.search(sqli_pattern, clean_line) else "No"
## Write the data row to the CSV
writer.writerow([source_ip, sqli_detected, clean_line])
print("Report generation finished.")
if __name__ == "__main__":
log_file = "access.log"
report_file = "report.csv"
analyze_logs(log_file, report_file)
ファイルを保存してスクリプトを実行します。
python3 log_analyzer.py
レポート生成が完了したことを示すメッセージが表示されます。次に、CSV ファイルが作成されたことを確認し、cat コマンドで内容を確認しましょう。
cat report.csv
きれいに整形された CSV 出力が表示されるはずです。
Source IP,SQLi Detected,Raw Log
192.168.1.10,No,192.168.1.10 - - [10/Oct/2026:13:55:36 -0700] "GET /index.html HTTP/1.1" 200 2326
10.0.0.15,Yes,10.0.0.15 - - [10/Oct/2026:13:55:45 -0700] "GET /login.php?user=admin' OR '1'='1 HTTP/1.1" 200 5120
172.16.0.5,No,172.16.0.5 - - [10/Oct/2026:13:56:01 -0700] "POST /api/data HTTP/1.1" 404 134
192.168.1.22,Yes,192.168.1.22 - - [10/Oct/2026:13:56:12 -0700] "GET /products.php?id=1 UNION SELECT username, password FROM users HTTP/1.1" 200 4012
10.0.0.25,No,10.0.0.25 - - [10/Oct/2026:13:57:00 -0700] "GET /about.html HTTP/1.1" 200 1024
まとめ
この実験では、Python を使用して手動のログ確認から自動化されたログ分析へと移行することに成功しました。Python の組み込みファイル処理を使用してテキストファイルを 1 行ずつ読み込み、処理する方法を学びました。また、re モジュールを活用して、IP アドレスのような特定のデータ形式を抽出するための正規表現を適用しました。さらに、生のログデータに対して既知の SQL インジェクション攻撃のシグネチャを照合することで、セキュリティ検出ロジックを実装しました。最後に、csv モジュールを使用して構造化データを CSV レポートとしてエクスポートする練習を行い、結果を共有・分析しやすくしました。
これらのスキルは、セキュリティ自動化とデータ解析の基礎を形成するものであり、膨大なデータセットを効率的に扱うことを可能にします。



