Nginx 访问日志中的 Web 攻击行为分析实践

一、项目背景

在安全运营和应急响应工作中,Web 访问日志是判断攻击行为的重要依据。相比只看工具扫描结果,日志分析更接近真实工作场景:需要从大量请求中找出异常 IP、攻击路径、Payload、状态码变化和攻击时间线。

本文基于本地靶场和授权测试环境中的 Nginx 访问日志,模拟一次 Web 攻击行为分析流程,重点关注以下几类常见风险:

  • SQL 注入探测
  • XSS 探测
  • 目录扫描
  • 弱口令爆破
  • 敏感路径访问

目标不是单纯判断“有没有攻击”,而是整理出一份可用于安全运营和应急处置的分析结果。

二、环境说明

分析环境如下:

项目 内容
Web 服务 Nginx
日志文件 access.log
分析语言 Python
分析对象 本地靶场/授权环境访问日志
分析重点 攻击特征、异常 IP、攻击路径、状态码、时间线

Nginx 常见访问日志格式如下:

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

其中重点字段包括:

  • 访问 IP
  • 请求时间
  • 请求方法
  • URL 路径和参数
  • HTTP 状态码
  • User-Agent

三、攻击特征整理

1. SQL 注入特征

SQL 注入请求通常会在参数中出现数据库关键字、注释符、逻辑判断语句等内容,例如:

1
2
3
/news.php?id=1' and '1'='1
/user.php?id=1 union select 1,2,3
/search.php?q=test%27%20or%201=1--

常见特征包括:

  • URL 中出现 union select
  • 参数中出现 ' or '1'='1
  • 出现 --# 等 SQL 注释符
  • 请求频率较高,参数变化明显
  • 可能伴随 500、403、302、200 等状态码变化

2. XSS 特征

XSS 探测通常会在参数中插入 HTML 或 JavaScript 片段,例如:

1
2
/search.php?keyword=<script>alert(1)</script>
/comment.php?content=<img src=x onerror=alert(1)>

常见特征包括:

  • URL 中出现 <script>
  • 出现 onerroronload 等事件属性
  • 出现 javascript: 伪协议
  • 请求路径多集中在搜索、评论、留言等功能点

3. 目录扫描特征

目录扫描常见于攻击者的信息收集阶段,目的是发现后台、备份文件、配置文件、上传目录等敏感资源。

常见路径包括:

1
2
3
4
5
6
/admin/
/backup.zip
/.git/config
/phpinfo.php
/uploads/
/robots.txt

判断依据:

  • 单个 IP 在短时间内访问大量不同路径
  • 状态码以 404、403 为主
  • 请求路径具有明显字典扫描特征
  • User-Agent 可能为空,或出现扫描器特征

4. 弱口令爆破特征

弱口令爆破通常集中在登录接口,例如:

1
2
POST /login.php HTTP/1.1
POST /admin/login HTTP/1.1

判断依据:

  • 同一 IP 短时间内多次请求登录接口
  • 状态码可能持续为 200、302、401、403
  • 请求时间间隔较短
  • 失败次数明显异常

四、Python 分析脚本

下面脚本用于从 Nginx access.log 中提取异常请求,并按照攻击类型进行简单分类。

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
import re
from collections import Counter, defaultdict

log_file = "access.log"

patterns = {
"SQL注入": [
r"union\s+select",
r"or\s+1=1",
r"and\s+1=1",
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"/\.git",
r"/admin",
r"/phpinfo\.php",
r"/backup",
r"/config",
r"/uploads"
],
"弱口令爆破": [
r"POST\s+/login",
r"POST\s+/admin",
r"POST\s+/user/login"
]
}

ip_counter = Counter()
attack_counter = Counter()
path_counter = Counter()
timeline = defaultdict(list)

log_pattern = re.compile(
r'(?P<ip>\S+) \S+ \S+ \[(?P<time>.*?)\] "(?P<request>.*?)" (?P<status>\d+) (?P<size>\S+)'
)

def detect_attack(request):
request_lower = request.lower()
result = []
for attack_type, rules in patterns.items():
for rule in rules:
if re.search(rule, request_lower):
result.append(attack_type)
break
return result

with open(log_file, "r", encoding="utf-8", errors="ignore") as f:
for line in f:
match = log_pattern.search(line)
if not match:
continue

ip = match.group("ip")
time = match.group("time")
request = match.group("request")
status = match.group("status")

attacks = detect_attack(request)
if attacks:
ip_counter[ip] += 1
path_counter[request] += 1
for attack in attacks:
attack_counter[attack] += 1
timeline[ip].append((time, attack, status, request))

print("[+] 攻击类型统计")
for attack, count in attack_counter.most_common():
print(f"{attack}: {count}")

print("\n[+] 异常 IP TOP 10")
for ip, count in ip_counter.most_common(10):
print(f"{ip}: {count}")

print("\n[+] 高频异常请求 TOP 10")
for path, count in path_counter.most_common(10):
print(f"{count} {path}")

print("\n[+] 攻击时间线")
for ip, events in timeline.items():
print(f"\nIP: {ip}")
for time, attack, status, request in events[:10]:
print(f"{time} [{attack}] status={status} {request}")

五、分析结果示例

脚本输出示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[+] 攻击类型统计
敏感路径访问: 48
SQL注入: 19
XSS探测: 11
弱口令爆破: 9

[+] 异常 IP TOP 10
192.168.56.103: 37
192.168.56.109: 22
192.168.56.120: 18

[+] 高频异常请求 TOP 10
12 GET /admin/ HTTP/1.1
8 GET /.git/config HTTP/1.1
6 GET /search.php?q=<script>alert(1)</script> HTTP/1.1
5 GET /news.php?id=1 union select 1,2,3 HTTP/1.1

从结果可以看出:

  • 192.168.56.103 请求次数最多,存在明显目录扫描行为。
  • /.git/config/admin//backup.zip 等敏感路径被多次访问。
  • 部分 URL 参数中出现 SQL 注入和 XSS Payload。
  • 登录接口存在连续 POST 请求,需要进一步结合认证日志判断是否存在爆破成功。

六、攻击时间线

根据日志时间字段,可以整理出攻击过程:

时间 行为 说明
10:12 目录扫描 访问 /admin//backup.zip/.git/config
10:15 SQL 注入探测 id 参数构造注入 Payload
10:19 XSS 探测 对搜索参数插入脚本标签
10:23 登录爆破 /login.php 连续发送 POST 请求
10:30 敏感文件探测 访问配置文件和上传目录

该时间线说明攻击者先进行信息收集,再尝试漏洞探测,最后对登录口进行弱口令尝试,符合常见 Web 攻击流程。

七、影响范围判断

本次日志中暂未发现明确的 Getshell 迹象,但存在以下风险:

  • 后台登录入口暴露,存在弱口令爆破风险。
  • 敏感路径被扫描,可能泄露配置文件或备份文件。
  • 参数存在注入探测行为,需要确认后端是否做了过滤和参数化查询。
  • 搜索、评论等输入点存在 XSS 探测,需要检查输出编码。
  • 如果攻击请求返回 200 或 500,需要优先复查对应接口。

进一步排查建议:

  • 检查 Web 根目录是否存在异常文件。
  • 检查最近修改的 PHP、JSP、ASP 文件。
  • 检查 Nginx、应用、数据库日志是否存在异常。
  • 检查系统用户、计划任务、启动项和可疑进程。
  • 对高危 IP 做临时封禁或访问频率限制。

八、处置建议

1. Web 层加固

  • 后台入口限制访问 IP。
  • 登录接口增加验证码或频率限制。
  • 禁止访问 .git、备份文件、配置文件等敏感资源。
  • 上传目录禁止脚本执行。
  • 对异常 User-Agent 和高频请求做限速。

2. 代码层修复

  • SQL 查询使用预编译语句。
  • 用户输入统一校验和过滤。
  • 页面输出进行 HTML 编码。
  • 文件上传限制后缀、MIME 类型和文件内容。
  • 重要接口增加鉴权和权限校验。

3. 运营侧监控

  • 对 404、403 高频 IP 建立告警规则。
  • 对登录失败次数设置阈值告警。
  • 对 SQL 注入、XSS、目录扫描关键字建立检测规则。
  • 定期归档日志,便于追溯攻击时间线。
  • 将封禁、通知、复盘流程文档化。

九、总结

通过这次日志分析,可以看到安全运营工作并不是只依赖工具扫描,而是要把日志、攻击特征、时间线、影响范围和处置建议串起来。

本项目主要完成了以下内容:

  • 梳理常见 Web 攻击在 Nginx 日志中的表现。
  • 使用 Python 提取异常 IP、攻击路径、Payload 和状态码。
  • 根据日志整理攻击时间线。
  • 给出 Web 层、代码层和运营侧的加固建议。

这类项目适合作为安全运营、应急响应和渗透测试实习的博客材料。后续可以继续完善脚本,例如加入 CSV 导出、IP 归属地查询、图表统计和自动生成分析报告。