日志分析

​ 摘抄

​ 日志分析是通过对网络设备、操作系统、应用程序等产生的日志进行收集、处理、解析和挖掘,以发现潜在的安全威胁、攻击行为,以及对已发生的安全事件进行溯源和复盘。

Windows日志分析

Windows 中三类日志:

  1. 系统日志

    1
    %SystemRoot%\System32\Winevt\Logs\System.evtx
  2. 应用程序日志

    1
    %SystemRoot%\System32\Winevt\Logs\Application.evtx
  3. 安全日志,主要查看的日志

    1
    %SystemRoot%\System32\Winevt\Logs\Security.evtx

对于Windows事件日志分析,不同的EVENT ID代表了不同的意义,摘录一些常见的安全事件的说明:

事件ID说明
4624登录成功
4625登录失败
4634注销成功
4647用户启动的注销
4672使用超级用户(如管理员)进行登录
4720创建用户

每个成功登录的事件都会标记一个登录类型,不同登录类型代表不同的方式:

登录类型描述说明
2交互式登录(Interactive)用户在本地进行登录。
3网络(Network)最常见的情况就是连接到共享文件夹或共享打印机时。
4批处理(Batch)通常表明某计划任务启动。
5服务(Service)每种服务都被配置在某个特定的用户账号下运行。
7解锁(Unlock)屏保解锁。
8网络明文(NetworkCleartext)登录的密码在网络上是通过明文传输的,如FTP。
9新凭证(NewCredentials)使用带/Netonly参数的RUNAS命令运行一个程序。
10远程交互,(RemoteInteractive)通过终端服务、远程桌面或远程协助访问计算机。
11缓存交互(CachedInteractive)以一个域用户登录而又没有域控制器可用

关于更多EVENT ID,详见微软官方网站上找到了“Windows Vista 和 Windows Server 2008 中的安全事件的说明”。

原文链接 :https://support.microsoft.com/zh-cn/help/977519/description-of-security-events-in-windows-7-and-in-windows-server-2008

Log Parser

Log Parser(是微软公司出品的日志分析工具,它功能强大,使用简单,可以分析基于文本的日志文件、XML 文件、CSV(逗号分隔符)文件,以及操作系统的事件日志、注册表、文件系统、Active Directory。它可以像使用 SQL 语句一样查询分析这些数据,甚至可以把分析结果以各种图表的形式展现出来。

Log Parser 2.2下载地址:https://www.microsoft.com/en-us/download/details.aspx?id=24659

Log Parser 使用示例:https://mlichtenberg.wordpress.com/2011/02/03/log-parser-rocks-more-than-50-examples/

基本查询结构

1
Logparser.exe –i:EVT –o:DATAGRID "SELECT * FROM c:\xx.evtx"

Linux日志分析

前言

Linux系统拥有非常灵活和强大的日志功能,可以保存几乎所有的操作记录,并可以从中检索出我们需要的信息。 本文简介一下Linux系统日志及日志分析技巧。

日志简介

日志默认存放位置:/var/log/

查看日志配置情况:more /etc/rsyslog.conf

日志文件说明
/var/log/cron记录了系统定时任务相关的日志
/var/log/cups记录打印信息的日志
/var/log/dmesg记录了系统在开机时内核自检的信息,也可以使用dmesg命令直接查看内核自检信息
/var/log/mailog记录邮件信息
/var/log/message记录系统重要信息的日志。这个日志文件中会记录Linux系统的绝大多数重要信息,如果系统出现问题时,首先要检查的就应该是这个日志文件
/var/log/btmp记录错误登录日志,这个文件是二进制文件,不能直接vi查看,而要使用lastb命令查看
/var/log/lastlog记录系统中所有用户最后一次登录时间的日志,这个文件是二进制文件,不能直接vi,而要使用lastlog命令查看
/var/log/wtmp永久记录所有用户的登录、注销信息,同时记录系统的启动、重启、关机事件。同样这个文件也是一个二进制文件,不能直接vi,而需要使用last命令来查看
/var/log/utmp记录当前已经登录的用户信息,这个文件会随着用户的登录和注销不断变化,只记录当前登录用户的信息。同样这个文件不能直接vi,而要使用w,who,users等命令来查询
/var/log/secure记录验证和授权方面的信息,只要涉及账号和密码的程序都会记录,比如SSH登录,su切换用户,sudo授权,甚至添加用户和修改用户密码都会记录在这个日志文件中

比较重要的几个日志:
登录失败记录:/var/log/btmp //lastb
最后一次登录:/var/log/lastlog //lastlog
登录成功记录: /var/log/wtmp //last
登录日志记录:/var/log/secure

​ 目前登录用户信息:/var/run/utmp //w、who、users

​ 历史命令记录:history
​ 仅清理当前用户: history -c

日志分析技巧

A、常用的shell命令

Linux下常用的shell命令如:find、grep 、egrep、awk、sed

小技巧:

1、grep显示前后几行信息:

1
2
3
4
5
6
	标准unix/linux下的grep通过下面參数控制上下文:
​ grep -C 5 foo file 显示file文件里匹配foo字串那行以及上下5行
​ grep -B 5 foo file 显示foo及前5行
​ grep -A 5 foo file 显示foo及后5行
​ 查看grep版本号的方法是
​ grep -V

2、grep 查找含有某字符串的所有文件

1
2
3
4
5
6
grep -rn "hello,world!" 
* : 表示当前目录所有文件,也可以是某个文件名
-r 是递归查找
-n 是显示行号
-R 查找所有文件包含子目录
-i 忽略大小写

3、如何显示一个文件的某几行:

1
2
cat input_file | tail -n +1000 | head -n 2000
#从第1000行开始,显示2000行。即显示1000~2999行

4、find /etc -name init

//在目录/etc中查找文件init

5、只是显示/etc/passwd的账户

`cat /etc/passwd |awk  -F ':'  '{print $1}'`  
//awk -F指定域分隔符为':',将记录按指定的域分隔符划分域,填充域,​$0则表示所有域,$1表示第一个域,​$n表示第n个域。

6、sed -i ‘153,$d’ .bash_history

删除历史操作记录,只保留前153行

B、日志分析技巧

A、/var/log/secure

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
1、定位有多少IP在爆破主机的root帐号:    
grep "Failed password for root" /var/log/secure | awk '{print $11}' | sort | uniq -c | sort -nr | more

定位有哪些IP在爆破:
grep "Failed password" /var/log/secure|grep -E -o "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"|uniq -c

爆破用户名字典是什么?
grep "Failed password" /var/log/secure|perl -e 'while($_=<>){ /for(.*?) from/; print "$1\n";}'|uniq -c|sort -nr

2、登录成功的IP有哪些:
grep "Accepted " /var/log/secure | awk '{print $11}' | sort | uniq -c | sort -nr | more

登录成功的日期、用户名、IP:
grep "Accepted " /var/log/secure | awk '{print $1,$2,$3,$9,$11}'

3、增加一个用户kali日志:
Jul 10 00:12:15 localhost useradd[2382]: new group: name=kali, GID=1001
Jul 10 00:12:15 localhost useradd[2382]: new user: name=kali, UID=1001, GID=1001, home=/home/kali
, shell=/bin/bash
Jul 10 00:12:58 localhost passwd: pam_unix(passwd:chauthtok): password changed for kali
#grep "useradd" /var/log/secure

4、删除用户kali日志:
Jul 10 00:14:17 localhost userdel[2393]: delete user 'kali'
Jul 10 00:14:17 localhost userdel[2393]: removed group 'kali' owned by 'kali'
Jul 10 00:14:17 localhost userdel[2393]: removed shadow group 'kali' owned by 'kali'
# grep "userdel" /var/log/secure

5、su切换用户:
Jul 10 00:38:13 localhost su: pam_unix(su-l:session): session opened for user good by root(uid=0)

sudo授权执行:
sudo -l
Jul 10 00:43:09 localhost sudo: good : TTY=pts/4 ; PWD=/home/good ; USER=root ; COMMAND=/sbin/shutdown -r now

2、/var/log/yum.log

软件安装升级卸载日志:

install gcc
1
2
3
4
5
6
7
8
9
yum install gcc

[root@bogon ~]# more /var/log/yum.log

Jul 10 00:18:23 Updated: cpp-4.8.5-28.el7_5.1.x86_64
Jul 10 00:18:24 Updated: libgcc-4.8.5-28.el7_5.1.x86_64
Jul 10 00:18:24 Updated: libgomp-4.8.5-28.el7_5.1.x86_64
Jul 10 00:18:28 Updated: gcc-4.8.5-28.el7_5.1.x86_64
Jul 10 00:18:28 Updated: libgcc-4.8.5-28.el7_5.1.i686

Web日志分析

Web日志

Web访问日志记录了Web服务器接收处理请求及运行时错误等各种原始信息。通过对WEB日志进行的安全分析,不仅可以帮助我们定位攻击者,还可以帮助我们还原攻击路径,找到网站存在的安全漏洞并进行修复。

我们来看一条Apache的访问日志:

127.0.0.1 - - [11/Jun/2018:12:47:22 +0800] "GET /login.html HTTP/1.1" 200 786 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36"

通过这条Web访问日志,我们可以清楚的得知用户在什么IP、什么时间、用什么操作系统、什么浏览器的情况下访问了你网站的哪个页面,是否访问成功。

本文通过介绍Web日志安全分析时的思路和常用的一些技巧。

日志分析技巧

在对WEB日志进行安全分析时,一般可以按照两种思路展开,逐步深入,还原整个攻击过程。

第一种:确定入侵的时间范围,以此为线索,查找这个时间范围内可疑的日志,进一步排查,最终确定攻击者,还原攻击过程。

第二种:攻击者在入侵网站后,通常会留下后门维持权限,以方便再次访问,我们可以找到该文件,并以此为线索来展开分析。

常用分析工具:

Window下,推荐用 EmEditor 进行日志分析,支持大文本,搜索效率还不错。

Linux下,使用Shell命令组合查询分析。

Shell+Linux命令实现日志分析,一般结合grep、awk等命令等实现了几个常用的日志分析统计技巧。

Apache日志分析技巧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1、列出当天访问次数最多的IP命令:
cut -d- -f 1 log_file|uniq -c | sort -rn | head -20

2、查看当天有多少个IP访问:
awk '{print $1}' log_file|sort|uniq|wc -l

3、查看某一个页面被访问的次数:
grep "/index.php" log_file | wc -l

4、查看每一个IP访问了多少个页面:
awk '{++S[$1]} END {for (a in S) print a,S[a]}' log_file

5、将每个IP访问的页面数进行从小到大排序:
awk '{++S[$1]} END {for (a in S) print S[a],a}' log_file | sort -n

6、查看某一个IP访问了哪些页面:
grep ^111.111.111.111 log_file| awk '{print $1,$7}'

7、去掉搜索引擎统计当天的页面:
awk '{print $12,$1}' log_file | grep ^\"Mozilla | awk '{print $2}' |sort | uniq | wc -l

8、查看2018年6月21日14时这一个小时内有多少IP访问:
awk '{print $4,$1}' log_file | grep 21/Jun/2018:14 | awk '{print $2}'| sort | uniq | wc -l

日志分析案例

Web日志分析实例:通过nginx代理转发到内网某服务器,内网服务器某站点目录下被上传了多个图片木马,虽然II7下不能解析,但还是想找出谁通过什么路径上传的。

在这里,我们遇到了一个问题:由于设置了代理转发,只记录了代理服务器的ip,并没有记录访问者IP?这时候,如何去识别不同的访问者和攻击源呢?

这是管理员日志配置不当的问题,但好在我们可以通过浏览器指纹来定位不同的访问来源,还原攻击路径。

1、定位攻击源

首先访问图片木马的记录,只找到了一条,由于所有访问日志只记录了代理IP,并不能通过IP来还原攻击路径,这时候,可以利用浏览器指纹来定位。

浏览器指纹:

Mozilla/4.0+(compatible;+MSIE+7.0;+Windows+NT+6.1;+WOW64;+Trident/7.0;+SLCC2;+.NET+CLR+2.0.50727;+.NET+CLR+3.5.30729;+.NET+CLR+3.0.30729;+.NET4.0C;+.NET4.0E)

2、搜索相关日志记录

通过筛选与该浏览器指纹有关的日志记录,可以清晰地看到攻击者的攻击路径。

3、对找到的访问日志进行解读,攻击者大致的访问路径如下:

1
2
3
4
5
A、攻击者访问首页和登录页
B、攻击者访问MsgSjlb.aspx和MsgSebd.aspx
C、攻击者访问Xzuser.aspx
D、攻击者多次POST(怀疑通过这个页面上传模块缺陷)
E、攻击者访问了图片木马

打开网站,访问Xzuser.aspx,确认攻击者通过该页面的进行文件上传了图片木马,同时,发现网站了存在越权访问漏洞,攻击者访问特定URL,无需登录即可进入后台界面。通过日志分析找到网站的漏洞位置并进行修复。

日志统计分析技巧

统计爬虫:

1
grep -E 'Googlebot|Baiduspider'  /www/logs/access.2019-02-23.log | awk '{ print $1 }' | sort | uniq

统计浏览器:

1
cat /www/logs/access.2019-02-23.log | grep -v -E 'MSIE|Firefox|Chrome|Opera|Safari|Gecko|Maxthon' | sort | uniq -c | sort -r -n | head -n 100		

IP 统计:

1
2
3
4
5
6
7
8
9
10
11
grep '23/May/2019' /www/logs/access.2019-02-23.log | awk '{print $1}' | awk -F'.' '{print $1"."$2"."$3"."$4}' | sort | uniq -c | sort -r -n | head -n 10
2206 219.136.134.13
1497 182.34.15.248
1431 211.140.143.100
1431 119.145.149.106
1427 61.183.15.179
1427 218.6.8.189
1422 124.232.150.171
1421 106.187.47.224
1420 61.160.220.252
1418 114.80.201.18

统计网段:

1
cat /www/logs/access.2019-02-23.log | awk '{print $1}' | awk -F'.' '{print $1"."$2"."$3".0"}' | sort | uniq -c | sort -r -n | head -n 200			

统计域名:

1
cat  /www/logs/access.2019-02-23.log |awk '{print $2}'|sort|uniq -c|sort -rn|more		

HTTP Status:

1
2
3
4
5
cat  /www/logs/access.2019-02-23.log |awk '{print $9}'|sort|uniq -c|sort -rn|more
5056585 304
1125579 200
7602 400
5 301

URL 统计:

1
cat  /www/logs/access.2019-02-23.log |awk '{print $7}'|sort|uniq -c|sort -rn|more			

文件流量统计:

1
2
3
cat /www/logs/access.2019-02-23.log |awk '{sum[$7]+=$10}END{for(i in sum){print sum[i],i}}'|sort -rn|more

grep ' 200 ' /www/logs/access.2019-02-23.log |awk '{sum[$7]+=$10}END{for(i in sum){print sum[i],i}}'|sort -rn|more

URL访问量统计:

1
cat /www/logs/access.2019-02-23.log | awk '{print $7}' | egrep '\?|&' | sort | uniq -c | sort -rn | more			

脚本运行速度:

查出运行速度最慢的脚本

1
grep -v 0$ /www/logs/access.2019-02-23.log | awk -F '\" ' '{print $4" " $1}' web.log | awk '{print $1" "$8}' | sort -n -k 1 -r | uniq > /tmp/slow_url.txt			

IP, URL 抽取:

1
# tail -f /www/logs/access.2019-02-23.log | grep '/test.html' | awk '{print $1" "$7}'			

Shell

自动封堵恶意ssh IP

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
#!/bin/bash

# 定义阈值:失败登录次数超过此值则封禁(可根据需求调整)
THRESHOLD=5

# 提取lastb中失败登录的IP,并统计次数(排除本地IP)
awk '/ssh:notty/ {print $3}' /var/log/btmp | grep -v -E '^127\.|^192\.168\.|^10\.|^172\.(1[6-9]|2[0-9]|3[0-1])\.' | sort | uniq -c | sort -nr > /tmp/ssh_failed.tmp

# 遍历结果,封禁超过阈值的IP
while read -r COUNT IP; do
if [ $COUNT -gt $THRESHOLD ]; then
# 检查IP是否已被封禁
if ! ufw status | grep -q "$IP"; then
echo "封禁恶意IP: $IP(失败次数: $COUNT)"
ufw insert 1 deny from $IP to any # 插入规则到防火墙最前面
else
echo "IP $IP 已在封禁列表中"
fi
fi
done < /tmp/ssh_failed.tmp

# 清理临时文件
rm -f /tmp/ssh_failed.tmp

# 重载防火墙规则
ufw reload > /dev/null

echo "恶意IP封禁处理完成"

可自定义时长,自定义封禁阈值,24小时自动解封,

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
#!/bin/bash

# ==============================================
# 配置区域 - 可根据需求自定义
# ==============================================
DURATION=3600 # 监控时长(秒),如3600=1小时内的记录
FAIL_COUNT_THRESHOLD=5 # 失败次数阈值,超过此值则封禁
BAN_RECORD_FILE="/var/log/ban_ips.record" # 封禁记录文件(用于自动解封)
BAN_DURATION=86400 # 封禁时长(秒),86400=24小时
# ==============================================

# 确保记录文件存在
if [ ! -f "$BAN_RECORD_FILE" ]; then
touch "$BAN_RECORD_FILE"
chmod 600 "$BAN_RECORD_FILE" # 限制权限,仅root可读写
fi

# --------------------------
# 第一步:自动解封过期IP
# --------------------------
echo "开始检查过期封禁记录..."
current_time=$(date +%s) # 当前时间戳(秒)

# 遍历封禁记录,检查是否过期
while IFS= read -r line; do
if [ -n "$line" ]; then
ban_ip=$(echo "$line" | awk '{print $1}')
ban_time=$(echo "$line" | awk '{print $2}')
expire_time=$((ban_time + BAN_DURATION))

# 若已过期,解除封禁并从记录中删除
if [ $current_time -ge $expire_time ]; then
echo "解除过期封禁: $ban_ip"
# 删除防火墙规则
ufw delete deny from "$ban_ip" to any > /dev/null 2>&1
# 从记录文件中移除该IP
sed -i "/^$ban_ip /d" "$BAN_RECORD_FILE"
fi
fi
done < "$BAN_RECORD_FILE"


# --------------------------
# 第二步:提取指定时长内的失败IP
# --------------------------
echo "分析最近 $((DURATION/3600)) 小时的登录失败记录..."

# 从lastb提取指定时间范围内的SSH失败登录IP(排除局域网IP)
# --since参数格式:"${DURATION} seconds ago"
failed_ips=$(lastb --since "${DURATION} seconds ago" | \
awk '/ssh:notty/ {print $3}' | \
grep -v -E '^127\.|^192\.168\.|^10\.|^172\.(1[6-9]|2[0-9]|3[0-1])\.' | \
sort | uniq -c | sort -nr)

# 若没有符合条件的记录,直接退出
if [ -z "$failed_ips" ]; then
echo "未发现超过阈值的恶意IP"
exit 0
fi


# --------------------------
# 第三步:封禁超过阈值的IP
# --------------------------
echo "开始处理恶意IP..."
echo "$failed_ips" | while read -r count ip; do
# 检查是否超过失败次数阈值
if [ $count -gt $FAIL_COUNT_THRESHOLD ]; then
# 检查该IP是否已在封禁记录中
if ! grep -q "^$ip " "$BAN_RECORD_FILE"; then
echo "封禁恶意IP: $ip($count次失败尝试)"
ufw insert 1 deny from "$ip" to any > /dev/null 2>&1
# 记录封禁时间(时间戳)到文件
echo "$ip $(date +%s)" >> "$BAN_RECORD_FILE"
else
echo "IP $ip 已在封禁列表中(无需重复操作)"
fi
fi
done


# 重载防火墙规则
ufw reload > /dev/null 2>&1

echo "处理完成!当前封禁记录已更新"

自动封堵 Web 恶意 IP

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
#!/bin/bash
# 功能:监控Web访问,对"访问次数超限""含恶意关键字访问超限"的IP执行24小时封禁
# 适用:基于Nginx日志(其他Web服务器日志可调整格式解析部分)

# ==============================================
# 【核心配置区 - 根据需求自定义】
# ==============================================
DURATION=3600 # 监控时长(秒),例如3600=最近1小时
ACCESS_THRESHOLD=50 # 普通访问次数阈值:超过此次数则封禁(如1小时内访问50次)
MALICIOUS_KEYWORDS=("union select" "exec(" "drop table" "/etc/passwd" "xp_cmdshell") # 恶意关键字列表(支持SQL注入、命令执行等特征)
KEYWORD_THRESHOLD=3 # 关键字触发阈值:含任意关键字的访问超过此次数则封禁
BAN_RECORD_FILE="/var/log/web_ban_records.txt" # 封禁记录文件(存储IP和封禁时间)
BAN_DURATION=86400 # 封禁时长(秒),86400=24小时
NGINX_LOG="/var/log/nginx/access.log" # Nginx访问日志路径(根据实际修改)
# ==============================================

# 确保封禁记录文件存在并限制权限
if [ ! -f "$BAN_RECORD_FILE" ]; then
touch "$BAN_RECORD_FILE"
chmod 600 "$BAN_RECORD_FILE" # 仅root可读写,避免权限泄露
fi

# --------------------------
# 第一步:自动解封过期IP(24小时后)
# --------------------------
echo "===== 开始检查过期封禁 ====="
current_timestamp=$(date +%s) # 当前时间戳(秒)

# 遍历封禁记录,处理过期IP
while IFS= read -r line; do
if [ -n "$line" ]; then
ban_ip=$(echo "$line" | awk '{print $1}')
ban_time=$(echo "$line" | awk '{print $2}')
expire_time=$((ban_time + BAN_DURATION))

# 若当前时间已超过过期时间,解除封禁
if [ $current_timestamp -ge $expire_time ]; then
echo "解除封禁过期IP: $ban_ip(封禁时长已满24小时)"
# 删除防火墙规则(忽略不存在的规则错误)
ufw delete deny from "$ban_ip" to any > /dev/null 2>&1
# 从记录文件中移除该IP
sed -i "/^$ban_ip /d" "$BAN_RECORD_FILE"
fi
fi
done < "$BAN_RECORD_FILE"


# --------------------------
# 第二步:解析Nginx日志,提取监控时长内的访问记录
# --------------------------
echo -e "\n===== 解析最近 $((DURATION/3600)) 小时的Web访问日志 ====="

# 计算监控时间范围的起始时间戳(当前时间 - 监控时长)
start_timestamp=$((current_timestamp - DURATION))

# 定义函数:将Nginx日志时间转换为时间戳(适配格式:[05/Aug/2025:10:30:45 +0800])
log_to_timestamp() {
local log_time="$1"
# 去除日志时间中的[],转换为date命令可识别的格式(如"05/Aug/2025:10:30:45 +0800")
local clean_time=$(echo "$log_time" | sed 's/^\[//;s/\]$//')
# 转换为时间戳(秒)
date -d "$clean_time" +%s 2>/dev/null # 错误输出到空(避免无效时间报错)
}

# 从日志中提取符合时间范围的记录,并输出IP和请求内容(用于后续分析)
# Nginx日志格式说明(默认):$remote_addr - $remote_user [$time_local] "$request" $status ...
# 其中$request包含请求方法、路径、参数(如"GET /?id=1 union select..."
valid_records=$(awk -v start_ts="$start_timestamp" -v end_ts="$current_timestamp" '
{
ip = $1; # 客户端IP
log_time = $4; # 日志时间(如[05/Aug/2025:10:30:45 +0800])
request = $0; # 完整请求内容(用于匹配恶意关键字)

# 调用外部函数转换日志时间为时间戳
cmd = "log_to_timestamp \"" log_time "\""
cmd | getline ts;
close(cmd);

# 筛选时间范围内的有效记录(时间戳在[start_ts, end_ts]之间)
if (ts >= start_ts && ts <= end_ts) {
print ip "|" request; # 用|分隔IP和请求内容,便于后续处理
}
}
' "$NGINX_LOG")

# 若没有有效记录,直接退出
if [ -z "$valid_records" ]; then
echo "未发现符合时间范围的访问记录,退出执行"
exit 0
fi


# --------------------------
# 第三步:统计并筛选需要封禁的IP
# --------------------------
echo -e "\n===== 分析并筛选恶意IP ====="

# 临时文件存储待封禁IP(去重)
temp_ban_list=$(mktemp)

# 3.1 处理第一种情况:普通访问次数超过阈值(ACCESS_THRESHOLD)
echo "--- 检查普通访问次数超限的IP ---"
# 提取IP并统计次数(排除局域网IP)
access_count=$(echo "$valid_records" | cut -d'|' -f1 | \
grep -v -E '^127\.|^192\.168\.|^10\.|^172\.(1[6-9]|2[0-9]|3[0-1])\.' | \
sort | uniq -c | sort -nr)

# 筛选超过阈值的IP,加入待封禁列表
echo "$access_count" | while read -r count ip; do
if [ $count -gt $ACCESS_THRESHOLD ]; then
echo "发现高频访问IP: $ip($count次访问,超过阈值$ACCESS_THRESHOLD)"
echo "$ip" >> "$temp_ban_list"
fi
done


# 3.2 处理第二种情况:含恶意关键字的访问次数超过阈值(KEYWORD_THRESHOLD)
echo -e "\n--- 检查含恶意关键字的IP ---"
# 遍历每条记录,检查是否含恶意关键字,提取对应IP
keyword_records=$(echo "$valid_records" | while IFS='|' read -r ip request; do
# 检查请求内容是否包含任意恶意关键字(不区分大小写)
for kw in "${MALICIOUS_KEYWORDS[@]}"; do
if echo "$request" | grep -qi "$kw"; then
echo "$ip"; # 匹配到关键字,输出IP
break # 避免同一请求匹配多个关键字时重复计数
fi
done
done)

# 统计含关键字的IP访问次数,筛选超过阈值的IP,加入待封禁列表
echo "$keyword_records" | sort | uniq -c | sort -nr | while read -r count ip; do
if [ $count -gt $KEYWORD_THRESHOLD ]; then
echo "发现含恶意关键字IP: $ip($count次匹配,超过阈值$KEYWORD_THRESHOLD)"
echo "$ip" >> "$temp_ban_list"
fi
done


# --------------------------
# 第四步:执行封禁(去重并避免重复封禁)
# --------------------------
echo -e "\n===== 执行封禁操作 ====="
# 去重待封禁IP列表
sort -u "$temp_ban_list" > "$temp_ban_list.tmp" && mv "$temp_ban_list.tmp" "$temp_ban_list"

# 遍历待封禁IP,执行封禁并记录
while read -r ip; do
# 检查该IP是否已在封禁记录中
if ! grep -q "^$ip " "$BAN_RECORD_FILE"; then
echo "封禁IP: $ip"
ufw insert 1 deny from "$ip" to any > /dev/null 2>&1 # 插入防火墙规则(优先生效)
echo "$ip $current_timestamp" >> "$BAN_RECORD_FILE" # 记录封禁时间戳
else
echo "IP: $ip 已在封禁列表中,跳过"
fi
done < "$temp_ban_list"

# 清理临时文件
rm -f "$temp_ban_list"

# 重载防火墙规则
ufw reload > /dev/null 2>&1

echo -e "\n===== 操作完成 ====="
echo "当前封禁状态已更新,所有符合条件的IP已执行24小时封禁"