一页速查版(实战直接照抄)
# ===== 1. 连接层初筛 =====
ss -nt | awk 'NR>1 {print $5}' | sed 's/\[//;s/\]//' | sed 's/:[0-9]*$//' | sort | uniq -c | sort -rn | head -20
ss -nt | awk 'NR>1 {print $1}' | sort | uniq -c | sort -rn
# ===== 2. 找日志 =====
nginx -T 2>/dev/null | grep -i access_log
ls -l --time-style=full-iso /www/wwwlogs/*.log
# ===== 3. 日志 top IP(兼容 IPv4/IPv6)=====
tail -100000 /www/wwwlogs/xxx.access.log \
| grep -oE '([0-9]{1,3}(\.[0-9]{1,3}){3}|[0-9a-fA-F:]{6,})"?$' \
| tr -d '"' | sort | uniq -c | sort -rn | head -20
# ===== 4. 确认行为 =====
grep '<IP>' /www/wwwlogs/xxx.access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head
grep '<IP>' /www/wwwlogs/xxx.access.log | awk '{print $9}' | sort | uniq -c | sort -rn
grep '<IP>' /www/wwwlogs/xxx.access.log | awk '{print $4}' | cut -d: -f1-3 | sort | uniq -c | sort -rn | head
# ===== 5. 处置 =====
# Nginx: deny <IP>;
# iptables -I INPUT -s <IP> -j DROP
总流程概览
报警 → 判断攻击类型 → 连接层初筛 → 日志层定位 → 确认行为 → 封禁/限流
│ │ │
│ │ └─ 不知道哪个日志 → 先定位日志文件
│ └─ 日志为空/太大 → 用 ss/conntrack 兜底
└─ CPU/带宽/连接数/磁盘 哪个先爆
第 0 步:先搞清楚"报警是什么"
不同报警查法不同:
| 报警内容 | 重点查 |
|---|---|
| CPU 打满 | 慢请求、CC、PHP 进程 |
| 带宽打满 | 大文件、DDoS、盗链 |
| 连接数暴涨 | CC、SYN Flood |
| 磁盘写满 | 日志暴涨(本身就是攻击信号) |
| 502/504 增多 | PHP-FPM 打满、后端被压 |
| 后台被扫 | 403/404 暴涨 |
先看是什么报警,决定往哪个方向查。
第 1 步:连接层初筛(不依赖日志,最快)
不管你知不知道日志在哪,这一步都能做。
1.1 当前连接数最多的 IP
ss -nt | awk 'NR>1 {print $5}' \
| sed 's/\[//;s/\]//' | sed 's/:[0-9]*$//' \
| sort | uniq -c | sort -rn | head -20
判断:
-
某个 IP 几百上千 → 高度可疑
-
全是
172.68.x/104.x等 CF 段 → 流量走 CDN,真实 IP 在日志 XFF 里
1.2 连接状态分布
ss -nt | awk 'NR>1 {print $1}' | sort | uniq -c | sort -rn
判断:
-
ESTAB正常 -
大量
SYN-RECV→ SYN Flood -
大量
TIME-WAIT→ 短连接高频请求(CC 特征)
1.3 按端口看连接
ss -nt | awk 'NR>1 {print $4}' | sed 's/.*://' | sort | uniq -c | sort -rn | head
判断:哪个端口连接最多,就是被攻击的服务(80/443/3306...)。
1.4 实时看新连接
watch -n 1 "ss -nt | awk 'NR>1 {print \$5}' | sed 's/\[//;s/\]//' | sed 's/:[0-9]*\$//' | sort | uniq -c | sort -rn | head -10"
第 2 步:定位日志文件(不知道日志在哪时)
2.1 找 nginx 实际配的日志
nginx -T 2>/dev/null | grep -i access_log
注意:
-
access_log /dev/null;= 该站点日志被关 -
路径可能不是
/var/log/nginx/,宝塔常在/www/wwwlogs/
2.2 看哪些日志在实时写入
ls -l --time-style=full-iso /www/wwwlogs/*.log
时间戳在更新的就是活跃日志。
2.3 看 nginx 进程打开了哪些日志
lsof -p $(pgrep -d, nginx) 2>/dev/null | grep '\.log'
2.4 确认日志格式(关键,决定怎么切字段)
head -1 /www/wwwlogs/xxx.access.log | cat -A
要确认三件事:
-
第 1 列是不是真实 IP?
-
最后一列是不是 XFF?是 IPv4 还是 IPv6?
-
有没有被 CF/代理覆盖?
第 3 步:日志层定位 top IP
3.1 通用取真实 IP(兼容 IPv4/IPv6)
tail -100000 /www/wwwlogs/xxx.access.log \
| grep -oE '([0-9]{1,3}(\.[0-9]{1,3}){3}|[0-9a-fA-F:]{6,})"?$' \
| tr -d '"' \
| sort | uniq -c | sort -rn | head -20
为什么不直接 awk '{print $1}':
-
有 CF 时
$1是 CF 回源 IP,不是真实用户 -
有 IPv6 时正则要兼容
-
最后一段可能是空,
$NF会取错
3.2 如果知道第 1 列就是真实 IP
tail -100000 /www/wwwlogs/xxx.access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -20
3.4 文件太大(GB 级)怎么办
不要直接 cat 全量,按顺序:
# 先看最近 10 万行
tail -100000 xxx.log | ...
# 再按时间段
awk -v d="$(date '+%d/%b/%Y:%H')" '$0 ~ d' xxx.log | ...
# 最后才考虑全量
3.5 多个站点一起查
for f in /www/wwwlogs/*.access.log; do
echo "===== $f ====="
tail -50000 "$f" 2>/dev/null \
| grep -oE '([0-9]{1,3}(\.[0-9]{1,3}){3}|[0-9a-fA-F:]{6,})"?$' \
| tr -d '"' | sort | uniq -c | sort -rn | head -10
done
第 4 步:确认可疑 IP 的行为(是不是攻击)
找到 top IP 后,不要急着封,先确认。
4.1 它请求了什么
grep '<IP>' /www/wwwlogs/xxx.access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -20
判断:
-
集中
/admin/、/login、/api/→ 爆破/扫描 -
集中同一个 URL → CC
-
随机路径 → 目录扫描
4.2 状态码分布
grep '<IP>' /www/wwwlogs/xxx.access.log | awk '{print $9}' | sort | uniq -c | sort -rn
判断:
-
大量 403/404 → 扫描被挡
-
大量 200 → 可能已突破或正常业务
-
大量 499/502 → 压后端
4.3 请求频率
grep '<IP>' /www/wwwlogs/xxx.access.log | awk '{print $4}' | cut -d: -f1-3 | sort | uniq -c | sort -rn | head
判断:1 秒内多次、持续几分钟 → 脚本行为。
4.4 UA / Referer
grep '<IP>' /www/wwwlogs/xxx.access.log | awk -F'"' '{print $6}' | sort | uniq -c | sort -rn | head
grep '<IP>' /www/wwwlogs/xxx.access.log | awk -F'"' '{print $4}' | sort | uniq -c | sort -rn | head
判断:
-
Referer 空、UA 伪装/多变 → 可疑
-
UA 是 curl/python/scanner → 工具
4.5 综合判断表
| 特征 | 结论 |
|---|---|
| 高频 + /admin/ + 403 | 后台爆破/扫描 |
| 高频 + 同一 URL + 200 | CC 攻击 |
| 高频 + 随机路径 + 404 | 目录扫描 |
| 多 IP 各来一点 | DDoS |
| 大量 SYN-RECV | SYN Flood |
| 单 IP 大量连接 | CC/爬虫 |
第 5 步:其他辅助维度
5.1 PHP-FPM 日志(默认不记 IP)
grep -r 'access.log' /etc/php-fpm.d/ /etc/php/*/fpm/pool.d/ 2>/dev/null
默认注释掉,开了才有。
5.2 应用日志
grep '<IP>' /path/to/app/storage/logs/*.log
第 6 步:处置
6.1 确认是攻击后封禁
Nginx 层(推荐,精准):
deny 103.252.19.100;
iptables(直连 IP 有效):
iptables -I INPUT -s 103.252.19.120 -j DROP
6.2 限流(防未来)
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
limit_req zone=req_limit burst=20 nodelay;
6.3 封段(谨慎)
iptables -I INPUT -s 103.252.19.0/24 -j DROP
核心原则
-
先连接层,再日志层------连接层不依赖日志,最快。
-
先定位日志文件,再统计------不要假设路径和格式。
-
先确认行为,再封禁------避免误封自己人/CDN。
-
注意 IPv4/IPv6 和 CF 回源 ------
$1不一定是真实 IP。 -
文件大先用 tail------GB 级日志全量扫会拖垮服务器。