基于 Python 的 Web 日志攻击特征提取工具

一、项目背景

在安全运营和应急响应中,Web 访问日志通常数量很大。如果完全手工分析,很容易漏掉异常请求。因此可以编写一个轻量级 Python 工具,对 Nginx/Apache 访问日志进行自动化提取,快速发现 SQL 注入、XSS、目录扫描、敏感文件访问和爆破行为。

本文实现一个本地日志分析脚本,适用于授权环境、靶场环境和自己的服务器日志分析,不用于未授权扫描或攻击。

二、工具目标

工具需要完成以下功能:

  • 读取 Web access.log。
  • 解析 IP、时间、请求方法、URL、状态码、User-Agent。
  • 根据规则匹配常见攻击特征。
  • 统计异常 IP、攻击类型和高频路径。
  • 输出攻击时间线。
  • 导出 CSV,方便后续整理报告。

三、日志格式

常见 Nginx access.log 格式如下:

1
192.168.56.101 - - [20/May/2026:10:12:33 +0800] "GET /index.php?id=1 HTTP/1.1" 200 1024 "-" "Mozilla/5.0"

本文脚本主要提取字段:

字段 说明
ip 访问来源 IP
time 请求时间
method 请求方法
url 请求路径和参数
status HTTP 状态码
user_agent 浏览器或工具标识

四、检测规则设计

1. SQL 注入

特征关键字:

1
2
3
4
5
6
union select
or 1=1
and 1=1
sleep(
benchmark(
information_schema

2. XSS

特征关键字:

1
2
3
4
5
<script
onerror
onload
javascript:
alert(

3. 敏感路径

特征路径:

1
2
3
4
5
6
/.git/config
/admin
/phpinfo.php
/backup.zip
/config.php
/.env

4. 命令执行

特征关键字:

1
2
3
4
5
6
7
cmd=
whoami
id
uname
/bin/bash
curl
wget

5. 弱口令爆破

判断思路:

  • 同一 IP 多次访问登录接口。
  • 请求方法多为 POST。
  • 短时间内出现大量 200、302、401、403。
  • 路径包含 login、admin、user 等关键词。

五、核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import argparse
import csv
import re
from collections import Counter, defaultdict
from urllib.parse import unquote


RULES = {
"SQL注入": [
r"union\s+select",
r"\bor\s+1=1\b",
r"\band\s+1=1\b",
r"sleep\s*\(",
r"benchmark\s*\(",
r"information_schema",
r"(--|%2d%2d|#|%23)"
],
"XSS探测": [
r"<script",
r"%3cscript",
r"onerror",
r"onload",
r"javascript:",
r"alert\s*\("
],
"敏感路径访问": [
r"/\.git/config",
r"/\.env",
r"/admin",
r"/phpinfo\.php",
r"/backup",
r"/config\.php"
],
"命令执行探测": [
r"cmd=",
r"whoami",
r"\bid\b",
r"uname",
r"/bin/bash",
r"curl\s+",
r"wget\s+"
],
"疑似登录爆破": [
r"post\s+/login",
r"post\s+/admin",
r"post\s+/user/login"
]
}


LOG_PATTERN = re.compile(
r'(?P<ip>\S+) \S+ \S+ \[(?P<time>.*?)\] '
r'"(?P<method>\S+)\s+(?P<url>\S+)\s+(?P<protocol>[^"]+)" '
r'(?P<status>\d+) (?P<size>\S+) '
r'"(?P<referer>[^"]*)" "(?P<ua>[^"]*)"'
)


def parse_line(line):
match = LOG_PATTERN.search(line)
if not match:
return None

data = match.groupdict()
data["decoded_url"] = unquote(data["url"])
return data


def detect(record):
text = f'{record["method"]} {record["decoded_url"]} {record["ua"]}'.lower()
hits = []

for attack_type, rules in RULES.items():
for rule in rules:
if re.search(rule, text, re.IGNORECASE):
hits.append(attack_type)
break

return hits


def analyze(log_path):
attack_counter = Counter()
ip_counter = Counter()
url_counter = Counter()
status_counter = Counter()
timeline = defaultdict(list)
rows = []

with open(log_path, "r", encoding="utf-8", errors="ignore") as f:
for line in f:
record = parse_line(line)
if not record:
continue

hits = detect(record)
if not hits:
continue

ip = record["ip"]
url = record["decoded_url"]
status = record["status"]

ip_counter[ip] += 1
url_counter[url] += 1
status_counter[status] += 1

for hit in hits:
attack_counter[hit] += 1
timeline[ip].append((record["time"], hit, record["method"], url, status))
rows.append({
"ip": ip,
"time": record["time"],
"attack_type": hit,
"method": record["method"],
"url": url,
"status": status,
"user_agent": record["ua"]
})

return attack_counter, ip_counter, url_counter, status_counter, timeline, rows


def write_csv(rows, output):
if not rows:
return

fields = ["ip", "time", "attack_type", "method", "url", "status", "user_agent"]
with open(output, "w", newline="", encoding="utf-8-sig") as f:
writer = csv.DictWriter(f, fieldnames=fields)
writer.writeheader()
writer.writerows(rows)


def print_top(title, counter, limit=10):
print(f"\n[+] {title}")
for key, value in counter.most_common(limit):
print(f"{value:>5} {key}")


def main():
parser = argparse.ArgumentParser(description="Web access log attack feature extractor")
parser.add_argument("-f", "--file", required=True, help="access.log path")
parser.add_argument("-o", "--output", default="result.csv", help="CSV output path")
args = parser.parse_args()

attack_counter, ip_counter, url_counter, status_counter, timeline, rows = analyze(args.file)

print_top("攻击类型统计", attack_counter)
print_top("异常 IP TOP 10", ip_counter)
print_top("异常 URL TOP 10", url_counter)
print_top("状态码统计", status_counter)

print("\n[+] 攻击时间线示例")
for ip, events in list(timeline.items())[:5]:
print(f"\nIP: {ip}")
for time, attack_type, method, url, status in events[:10]:
print(f"{time} [{attack_type}] {method} {url} status={status}")

write_csv(rows, args.output)
print(f"\n[+] CSV 已导出: {args.output}")


if __name__ == "__main__":
main()

六、使用方法

保存脚本为 log_analyzer.py,然后执行:

1
python3 log_analyzer.py -f access.log -o result.csv

输出示例:

1
2
3
4
5
6
7
8
9
10
11
[+] 攻击类型统计
28 敏感路径访问
15 SQL注入
8 XSS探测
6 疑似登录爆破

[+] 异常 IP TOP 10
31 192.168.56.103
12 192.168.56.109

[+] CSV 已导出: result.csv

CSV 结果可以用 Excel 打开,继续整理成应急响应报告。

七、结果分析思路

拿到脚本输出后,可以从以下几个角度分析:

1. 异常 IP

如果某个 IP 命中次数明显高于其他 IP,说明它可能正在进行扫描或攻击。

需要进一步查看:

  • 访问时间是否集中。
  • 是否存在扫描路径。
  • 是否访问过上传目录。
  • 是否访问过后台登录接口。

2. 状态码

状态码可以辅助判断攻击是否可能成功:

  • 大量 404:常见目录扫描。
  • 大量 403:访问被拦截或无权限。
  • 大量 500:可能触发后端异常。
  • 200:需要重点确认是否真的返回有效内容。
  • 302:可能涉及登录跳转或认证绕过。

3. 攻击时间线

攻击时间线可以帮助还原过程:

1
目录扫描 -> 漏洞探测 -> 上传文件 -> 访问 WebShell -> 命令执行

如果日志中出现连续行为,就需要结合 Web 目录文件修改时间继续排查。

八、工具局限

这个脚本是基于规则匹配的轻量工具,因此存在局限:

  • 混淆后的 Payload 可能绕过规则。
  • 正常业务请求可能误报。
  • 不同 Web 服务日志格式可能需要调整正则。
  • 无法直接判断攻击是否成功。
  • 需要结合文件、进程、系统日志做进一步确认。

因此它更适合作为初筛工具,而不是最终判断依据。

九、后续优化方向

后续可以继续增强:

  • 支持 Apache、Nginx 多种日志格式。
  • 增加 IP 归属地查询。
  • 输出 HTML 分析报告。
  • 增加时间窗口统计。
  • 对同一 IP 的攻击链自动排序。
  • 加入 WebShell 访问特征检测。
  • 做成命令行工具或 Web 可视化面板。

十、总结

本文实现了一个基于 Python 的 Web 日志攻击特征提取工具,可以快速从 access.log 中提取异常 IP、攻击类型、异常 URL、状态码和攻击时间线。

在实际安全运营中,这类工具适合用于日志初筛和报告辅助。它不能替代人工分析,但可以减少重复劳动,让分析人员更快定位重点 IP、重点路径和重点时间段。