线上被攻击排查实战:从异常流量到应急响应
2026/7/3大约 15 分钟
线上被攻击排查实战:从异常流量到应急响应
线上系统被攻击是每个运维和开发都可能面临的紧急事件。本文从异常流量发现、攻击类型识别、应急响应处置到 WAF 防护配置,完整还原一次线上攻击事件的排查与处置全过程。
一、问题背景
某周五下午 14:32,监控系统连续触发多条告警:
[告警] API 网关 QPS 突增: 35,000 (正常: 3,000) 增长 11.7 倍
[告警] 5xx 错误率: 45% (正常: 0.1%)
[告警] 数据库 CPU: 98% (阈值: 80%)
[告警] 服务器 CPU: 95% (所有节点)
[告警] 入站流量: 2.3 Gbps (正常: 200 Mbps)
[告警] 登录接口失败率: 78%同时客服收到大量用户反馈:网站访问缓慢、登录失败、页面白屏。
二、攻击发现
2.1 异常流量发现手段
┌──────────────────────────────────────────────────────────────────┐
│ 攻击发现手段体系 │
├─────────────────┬────────────────────────────────────────────────┤
│ 发现手段 │ 关键指标 │
├─────────────────┼────────────────────────────────────────────────┤
│ QPS 监控 │ QPS 突增、异常 URL 模式 │
│ 流量监控 │ 入站带宽突增、某个 IP 流量异常 │
│ 错误率监控 │ 5xx 突增、4xx 模式变化 │
│ 资源监控 │ CPU/内存/连接数飙升 │
│ 日志分析 │ 异常 User-Agent、异常请求路径 │
│ WAF 告警 │ SQL 注入/XSS 检测、CC 攻击告警 │
│ 业务监控 │ 登录失败率、验证码请求量 │
└─────────────────┴────────────────────────────────────────────────┘2.2 第一时间确认攻击
# 1. 查看 QPS 来源分布
tail -f /var/log/nginx/access.log | \
awk '{print $1}' | \
sort | uniq -c | sort -rn | head -20
# 输出示例:
# 15234 10.0.1.100 ← 内部 SLB(正常)
# 12345 185.220.1.50 ← 异常 IP!单 IP 1.2万次请求
# 8920 45.137.21.80 ← 异常 IP!
# 6789 91.243.59.120 ← 异常 IP!
# 2. 查看异常请求模式
tail -10000 /var/log/nginx/access.log | \
awk '{print $7}' | \
sort | uniq -c | sort -rn | head -20
# 输出示例:
# 12345 /api/login?username=admin&password=admin123
# 8920 /api/login?username=admin&password=password
# 6789 /api/login?username=root&password=123456
# 5000 /wp-admin/
# 3000 /.env
# 500 /api/user?id=1' OR '1'='1
# 发现多种攻击模式!2.3 攻击特征快速画像
# 3. 查看异常 User-Agent
tail -10000 /var/log/nginx/access.log | \
awk -F'"' '{print $6}' | \
sort | uniq -c | sort -rn | head -10
# 输出示例:
# 15234 python-requests/2.28.0 ← 脚本攻击!
# 8920 curl/7.68.0 ← 脚本攻击!
# 6789 Mozilla/4.0 (compatible; bot) ← 伪造爬虫
# 1234 Mozilla/5.0 (Windows NT 10.0) ← 正常用户
# 4. 查看请求时间分布
tail -50000 /var/log/nginx/access.log | \
awk '{print $4}' | \
cut -c1-20 | \
sort | uniq -c
# 输出示例:
# 14:32:00 500 ← 攻击开始
# 14:33:00 3500
# 14:34:00 8000
# 14:35:00 15000 ← 峰值
# 14:36:00 12000三、攻击类型识别
3.1 攻击类型决策树
异常流量
│
┌─────────────┼──────────────┐
▼ ▼ ▼
流量巨大 请求模式异常 内容异常
(Gbps级) (高频相同URL) (恶意payload)
│ │ │
▼ ▼ ▼
DDoS CC 攻击 应用层攻击
│ │ │
┌─────┤ ┌────┤ ┌────┼────┐
▼ ▼ ▼ ▼ ▼ ▼ ▼
SYN UDP 暴力 接口 SQL XSS 命令
Flood Flood 登录 爬虫 注入 注入3.2 DDoS 攻击识别
# DDoS 特征:大量伪造 IP 发送请求,耗尽带宽和连接
# 1. 查看连接数
netstat -an | grep :443 | grep ESTABLISHED | wc -l
# 输出: 65000 ← 正常 5000 左右,异常!
# 2. 查看 SYN_FLOOD(半连接)
netstat -an | grep :443 | grep SYN_RECV | wc -l
# 输出: 45000 ← 大量 SYN_RECV,SYN Flood 攻击!
# 3. 查看来源 IP 分布
netstat -an | grep :443 | grep SYN_RECV | \
awk '{print $5}' | cut -d: -f1 | \
sort | uniq -c | sort -rn | head -20
# 4. 查看带宽使用
iftop -i eth0 -n -N
# 如果入站带宽接近网卡上限,大概率 DDoS
# DDoS 类型判断:
# SYN Flood: 大量 SYN_RECV,半连接队列满
# UDP Flood: 大量 UDP 流量
# HTTP Flood: 大量 HTTP 请求(应用层 DDoS)
# 慢速攻击: 连接时间长,消耗连接数但 QPS 不高3.3 CC 攻击识别
# CC 攻击特征:模拟正常用户请求,但高频访问耗尽服务器资源
# 1. 高频访问同一 URL 的 IP
tail -100000 /var/log/nginx/access.log | \
awk '{print $1, $7}' | \
sort | uniq -c | sort -rn | head -30
# 输出示例:
# 15234 185.220.1.50 /api/search?q=test ← 同一IP+URL高频访问
# 8920 45.137.21.80 /api/search?q=test
# 6789 91.243.59.120 /api/product/list
# 2. 查看请求间隔(CC 攻击通常请求间隔非常短且固定)
tail -1000 /var/log/nginx/access.log | \
awk '{print $4}' | \
awk -F: '{print $1":"$2":"$3}' | \
uniq -c
# 3. 查看是否针对耗资源的接口
grep "/api/search" /var/log/nginx/access.log | \
awk '{print $NF}' | sort | uniq -c | sort -rn
# 响应时间分布
# 200ms x 5000 ← 正常
# 5000ms x 3000 ← 被攻击,接口变慢
# 504 x 2000 ← 超时3.4 SQL 注入识别
# SQL 注入特征:请求参数中包含 SQL 关键字
# 1. 搜索常见 SQL 注入特征
grep -iE "(union|select|insert|update|delete|drop|sleep|benchmark)" \
/var/log/nginx/access.log | \
awk '{print $7}' | head -20
# 输出示例:
# /api/user?id=1' OR '1'='1
# /api/user?id=1 UNION SELECT 1,2,3--
# /api/user?id=1; DROP TABLE users--
# /api/user?id=1' AND SLEEP(5)--
# /api/product?name=' OR 1=1#
# 2. 查看数据库慢查询
grep "SELECT" /var/log/mysql/slow.log | \
grep -iE "(sleep|benchmark|union|information_schema)" | head -10
# 输出示例:
# SELECT * FROM users WHERE id = 1 OR 1=1
# SELECT SLEEP(5)
# SELECT * FROM information_schema.tables3.5 XSS 攻击识别
# XSS 特征:请求参数中包含 JavaScript 代码
grep -iE "(<script|javascript:|onerror=|onload=|alert\()" \
/var/log/nginx/access.log | \
awk '{print $7}' | head -20
# 输出示例:
# /api/comment?content=<script>alert('XSS')</script>
# /api/profile?name=<img src=x onerror=alert(1)>
# /api/search?q=<script>document.cookie</script>
# /api/redirect?url=javascript:alert(1)3.6 攻击类型汇总对照表
┌──────────────────────────────────────────────────────────────────┐
│ 攻击类型识别对照表 │
├──────────┬──────────────────┬────────────────────────────────────┤
│ 攻击类型 │ 特征 │ 检测方法 │
├──────────┼──────────────────┼────────────────────────────────────┤
│ DDoS │ 带宽耗尽 │ netstat + 流量监控 │
│ SYN Flood│ 半连接满 │ SYN_RECV 数量 │
│ CC 攻击 │ 高频相同请求 │ IP+URL 频率分析 │
│ 暴力登录│ 大量登录失败 │ 登录失败率 + IP 分布 │
│ SQL 注入│ SQL 关键字 │ 请求参数正则匹配 │
│ XSS │ 脚本标签 │ 请求参数正则匹配 │
│ 命令注入│ 系统命令 │ 请求参数正则匹配 │
│ 目录遍历│ ../ 模式 │ 请求路径匹配 │
│ 越权访问│ ID 枚举 │ 同一用户访问大量不同 ID │
└──────────┴──────────────────┴────────────────────────────────────┘四、应急响应流程
4.1 应急响应 SOP
┌──────────────────────────────────────────────────────────────────┐
│ 应急响应标准操作流程 (SOP) │
├──────────────────────────────────────────────────────────────────┤
│ │
│ Step 1: 发现与确认 (0-5分钟) │
│ • 确认是否为攻击(排除正常流量突增) │
│ • 识别攻击类型 │
│ • 评估影响范围 │
│ • 启动应急响应小组 │
│ │
│ Step 2: 紧急止血 (5-15分钟) │
│ • 限流:降低单 IP / 全局限流阈值 │
│ • 封 IP:封禁攻击源 IP │
│ • 切流量:切到高防机房或 CDN │
│ • 降级:关闭非核心功能 │
│ │
│ Step 3: 分析与取证 (15-60分钟) │
│ • 分析攻击特征 │
│ • 提取攻击 payload │
│ • 确认是否有数据泄露 │
│ • 保存日志证据 │
│ │
│ Step 4: 防护加固 (1-4小时) │
│ • 配置 WAF 规则 │
│ • 增加验证码 │
│ • 修复应用漏洞 │
│ • 调整限流策略 │
│ │
│ Step 5: 恢复与复盘 (4-24小时) │
│ • 逐步解除限流 │
│ • 监控系统状态 │
│ • 编写事故报告 │
│ • 完善防护体系 │
│ │
└──────────────────────────────────────────────────────────────────┘4.2 紧急止血:限流
# Nginx 紧急限流配置
http {
# 定义限流区域
limit_req_zone $binary_remote_addr zone=emergency:10m rate=10r/s;
server {
# 紧急限流:每秒 10 个请求/IP
location /api/ {
limit_req zone=emergency burst=20 nodelay;
limit_req_status 429;
error_page 429 = @rate_limited;
}
location @rate_limited {
default_type application/json;
return 429 '{"code":429,"msg":"系统繁忙,请稍后重试"}';
}
}
}// 应用层紧急限流
@RestControllerAdvice
public class RateLimitAdvice {
private final RateLimiter globalLimiter = RateLimiter.create(2000);
private final Map<String, RateLimiter> ipLimiters = new ConcurrentHashMap<>();
@Around("@annotation(RateLimited)")
public Object rateLimit(ProceedingJoinPoint pjp) throws Throwable {
HttpServletRequest request = getCurrentRequest();
String ip = getClientIp(request);
// 单 IP 限流
RateLimiter ipLimiter = ipLimiters.computeIfAbsent(ip,
k -> RateLimiter.create(10));
if (!ipLimiter.tryAcquire()) {
throw new TooManyRequestsException("IP 限流: " + ip);
}
// 全局限流
if (!globalLimiter.tryAcquire()) {
throw new TooManyRequestsException("全局限流");
}
return pjp.proceed();
}
}4.3 紧急止血:封 IP
# 1. 提取攻击 IP 列表
tail -100000 /var/log/nginx/access.log | \
awk '{print $1}' | \
sort | uniq -c | sort -rn | \
awk '$1 > 1000 {print $2}' > /tmp/attack_ips.txt
# 2. 使用 ipset 高效封 IP(推荐)
ipset create blacklist hash:ip
while read ip; do
ipset add blacklist $ip
done < /tmp/attack_ips.txt
iptables -A INPUT -m set --match-set blacklist src -j DROP
# 3. Nginx 层面封 IP
# deny 185.220.1.50;
# deny 45.137.21.80;
# deny 91.243.59.0/24; # 封整个网段
# 4. 自动化封 IP 脚本
#!/bin/bash
THRESHOLD=500
LOG_FILE="/var/log/nginx/access.log"
BANNED_IPS="/etc/nginx/banned_ips.conf"
tail -10000 $LOG_FILE | \
awk '{print $1}' | \
sort | uniq -c | sort -rn | \
awk -v threshold=$THRESHOLD '$1 > threshold {print "deny " $2 ";"}' \
>> $BANNED_IPS
nginx -s reload4.4 紧急止血:切流量
# 当攻击流量太大,源站扛不住时,切换到高防/CDN
# 1. DNS 切换(最常用)
# 将域名 DNS 解析切换到高防 IP
dig +short api.example.com # 当前解析
# 修改 DNS 记录,指向高防 IP
# 2. BGP 切换(更快速)
# 将流量引导到清洗中心
# 3. CDN 切换
# Cloudflare: 开启 Under Attack 模式
# 阿里云: 开启 DDoS 高防 + WAF
# 4. 紧急下线非核心服务
# 降级非核心接口,保住核心交易链路4.5 降级保护
/**
* 攻击期间降级策略
*/
@Component
public class AttackModeHandler {
private volatile boolean attackMode = false;
@Scheduled(fixedRate = 10000)
public void checkAttackMode() {
double errorRate = metrics.getErrorRate();
int qps = metrics.getCurrentQPS();
if (errorRate > 0.3 || qps > 20000) {
if (!attackMode) {
enableAttackMode();
}
} else if (errorRate < 0.05 && qps < 5000) {
if (attackMode) {
disableAttackMode();
}
}
}
private void enableAttackMode() {
attackMode = true;
log.warn("⚠ 启动攻击模式!");
rateLimiter.setGlobalLimit(1000); // 降低限流阈值
captchaService.enableForAllEndpoints(); // 开启验证码
featureToggle.disable("recommendation"); // 关闭非核心功能
featureToggle.disable("search_advanced");
alertService.sendUrgent("检测到攻击,已启动攻击模式");
}
private void disableAttackMode() {
attackMode = false;
log.info("退出攻击模式,恢复正常");
rateLimiter.setGlobalLimit(5000);
captchaService.disableForAllEndpoints();
featureToggle.enableAll();
}
}五、取证分析
5.1 日志保全
# 攻击发生后,立即保全日志(防止被覆盖)
# 1. 打包关键日志
tar -czf /tmp/attack_evidence_$(date +%Y%m%d_%H%M%S).tar.gz \
/var/log/nginx/access.log \
/var/log/nginx/error.log \
/var/log/modsec_audit.log \
/var/log/mysql/slow.log
# 2. 计算哈希(防篡改)
sha256sum /tmp/attack_evidence_*.tar.gz > /tmp/evidence_hash.txt
# 3. 上传到安全存储
aws s3 cp /tmp/attack_evidence_*.tar.gz s3://security-evidence/5.2 攻击画像分析
#!/usr/bin/env python3
"""
攻击日志分析脚本
"""
import re
from collections import Counter, defaultdict
SQL_INJECTION_PATTERNS = [
r"union\s+select",
r"'\s*or\s*'?\d+'?\s*=\s*'?\d+",
r"sleep\s*\(",
r"benchmark\s*\(",
r"information_schema",
r"drop\s+table",
r"delete\s+from",
]
XSS_PATTERNS = [
r"<script",
r"javascript:",
r"onerror\s*=",
r"alert\s*\(",
r"document\.cookie",
r"eval\s*\(",
]
def analyze_log(log_file):
ip_counter = Counter()
url_counter = Counter()
ua_counter = Counter()
sql_injection = []
xss_attacks = []
attack_timeline = defaultdict(int)
with open(log_file, 'r') as f:
for line in f:
parts = line.split()
if len(parts) < 7:
continue
ip = parts[0]
timestamp = parts[3]
url = parts[6]
ua = line.split('"')[6] if '"' in line else ''
ip_counter[ip] += 1
url_counter[url] += 1
ua_counter[ua] += 1
url_lower = url.lower()
for pattern in SQL_INJECTION_PATTERNS:
if re.search(pattern, url_lower):
sql_injection.append({'ip': ip, 'url': url, 'time': timestamp})
break
for pattern in XSS_PATTERNS:
if re.search(pattern, url_lower):
xss_attacks.append({'ip': ip, 'url': url, 'time': timestamp})
break
time_key = timestamp[:17]
attack_timeline[time_key] += 1
print("=== 攻击分析报告 ===")
print(f"\nTop 10 攻击 IP:")
for ip, count in ip_counter.most_common(10):
print(f" {ip}: {count:,} 次")
print(f"\nTop 10 攻击 URL:")
for url, count in url_counter.most_common(10):
print(f" {url}: {count:,} 次")
print(f"\nUser-Agent 分布:")
for ua, count in ua_counter.most_common(5):
print(f" {ua}: {count:,} 次")
print(f"\nSQL 注入攻击: {len(sql_injection)} 次")
for attack in sql_injection[:5]:
print(f" [{attack['time']}] {attack['ip']} -> {attack['url']}")
print(f"\nXSS 攻击: {len(xss_attacks)} 次")
for attack in xss_attacks[:5]:
print(f" [{attack['time']}] {attack['ip']} -> {attack['url']}")
print(f"\n攻击时间线:")
for time_key, count in sorted(attack_timeline.items()):
bar = '#' * min(count // 100, 50)
print(f" {time_key} | {bar} {count:,}")
if __name__ == '__main__':
analyze_log('/var/log/nginx/access.log')六、WAF 规则配置
6.1 WAF 架构
┌──────────────────────────────────────────────────────────────────┐
│ WAF 部署架构 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ 用户请求 │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ CDN/SLB │ 第一层:DDoS 清洗 │
│ └────┬─────┘ │
│ ▼ │
│ ┌──────────┐ │
│ │ WAF │ 第二层:规则防护 │
│ │ ModSec │ - SQL 注入检测、XSS 检测 │
│ │ / 云 WAF │ - CC 频率控制、黑白名单 │
│ └────┬─────┘ │
│ ▼ │
│ ┌──────────┐ │
│ │ Nginx │ 第三层:限流 │
│ └────┬─────┘ │
│ ▼ │
│ ┌──────────┐ │
│ │ 应用服务器 │ 第四层:应用防护(参数校验、权限控制) │
│ └──────────┘ │
│ │
└──────────────────────────────────────────────────────────────────┘6.2 ModSecurity 规则配置
# ModSecurity 配置 (modsecurity.conf)
SecRuleEngine On
# ========== 1. SQL 注入防护 ==========
SecRule ARGS "(?i)(union\s+select|insert\s+into|delete\s+from|drop\s+table)" \
"id:1001,phase:2,deny,status:403,msg:'SQL Injection Attempt',\
logdata:'Matched: %{MATCHED_VAR}',severity:CRITICAL"
SecRule ARGS "(?i)('or'?\s*'?\d+'?\s*=\s*'?\d+|sleep\s*\(|benchmark\s*\()" \
"id:1002,phase:2,deny,status:403,msg:'SQL Injection Attempt'"
SecRule ARGS "(?i)information_schema|load_file|into\s+outfile" \
"id:1003,phase:2,deny,status:403,msg:'SQL Injection Attempt'"
# ========== 2. XSS 防护 ==========
SecRule ARGS "(?i)(<script|javascript:|onerror\s*=|onload\s*=|alert\s*\()" \
"id:1101,phase:2,deny,status:403,msg:'XSS Attack Attempt',\
logdata:'Matched: %{MATCHED_VAR}',severity:CRITICAL"
SecRule ARGS "(?i)(document\.cookie|eval\s*\()" \
"id:1102,phase:2,deny,status:403,msg:'XSS Attack Attempt'"
# ========== 3. CC 攻击防护 ==========
SecRule REQUEST_URI "@rx .*" \
"id:1201,phase:1,pass,nolog,initcol:ip=%{REMOTE_ADDR}"
SecRule IP:REQUEST_COUNT "@gt 120" \
"id:1202,phase:1,deny,status:429,msg:'Rate Limit Exceeded',\
setvar:ip.request_count=+1,expirevar:ip.request_count=60"
# ========== 4. 目录遍历防护 ==========
SecRule REQUEST_URI "(?i)(\.\./|\.\.\\|%2e%2e/)" \
"id:1301,phase:1,deny,status:403,msg:'Directory Traversal Attempt'"
# ========== 5. 命令注入防护 ==========
SecRule ARGS "(?i)(;|\||`|\$\(|system\s*\(|exec\s*\()" \
"id:1401,phase:2,deny,status:403,msg:'Command Injection Attempt'"6.3 Nginx 层防护
# Nginx 安全防护配置
server {
# 隐藏服务器版本
server_tokens off;
# 安全头
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
add_header Content-Security-Policy "default-src 'self'";
# 禁止访问敏感文件
location ~ /\. { deny all; }
location ~* \.(env|git|svn|htaccess)$ { deny all; }
# 限制请求体大小
client_max_body_size 10m;
# 限制请求方法
if ($request_method !~ ^(GET|POST|PUT|DELETE|HEAD|OPTIONS)$ ) {
return 405;
}
# IP 黑名单(动态更新)
include /etc/nginx/blocked_ips.conf;
# 恶意 User-Agent 封禁
map $http_user_agent $bad_agent {
default 0;
~*python 1;
~*curl 1;
~*sqlmap 1;
~*nikto 1;
~*nmap 1;
}
if ($bad_agent) { return 403; }
}6.4 应用层防护
/**
* 应用层安全防护过滤器
*/
@Component
public class SecurityFilter implements Filter {
private static final Pattern SQL_INJECTION = Pattern.compile(
"(?i)(union\\s+select|insert\\s+into|delete\\s+from|drop\\s+table|" +
"'\\s*or\\s*'?\\d+'?\\s*=\\s*'?\\d+|sleep\\s*\\(|benchmark\\s*\\(|" +
"information_schema|load_file)"
);
private static final Pattern XSS_PATTERN = Pattern.compile(
"(?i)(<script|javascript:|onerror\\s*=|onload\\s*=|" +
"alert\\s*\\(|document\\.cookie|eval\\s*\\()"
);
private static final Pattern PATH_TRAVERSAL = Pattern.compile(
"(\\.\\./|\\.\\.\\\\|%2e%2e/|%2e%2e\\\\)"
);
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
String queryString = req.getQueryString();
if (queryString != null) {
String decoded = URLDecoder.decode(queryString, StandardCharsets.UTF_8);
if (SQL_INJECTION.matcher(decoded).find()) {
logAttack(req, "SQL_INJECTION", decoded);
resp.setStatus(403);
resp.getWriter().write("Request blocked: SQL injection detected");
return;
}
if (XSS_PATTERN.matcher(decoded).find()) {
logAttack(req, "XSS", decoded);
resp.setStatus(403);
resp.getWriter().write("Request blocked: XSS detected");
return;
}
if (PATH_TRAVERSAL.matcher(decoded).find()) {
logAttack(req, "PATH_TRAVERSAL", decoded);
resp.setStatus(403);
return;
}
}
// 检测恶意 User-Agent
String userAgent = req.getHeader("User-Agent");
if (userAgent != null) {
String lower = userAgent.toLowerCase();
if (lower.contains("sqlmap") || lower.contains("nikto") ||
lower.contains("nmap") || lower.contains("masscan")) {
logAttack(req, "MALICIOUS_UA", userAgent);
resp.setStatus(403);
return;
}
}
chain.doFilter(request, response);
}
private void logAttack(HttpServletRequest req, String type, String payload) {
log.warn("安全告警! type={}, ip={}, url={}, payload={}",
type, req.getRemoteAddr(), req.getRequestURL(), payload);
securityAlertService.send(type, req.getRemoteAddr(),
req.getRequestURL().toString(), payload);
}
}七、长期防护体系
7.1 纵深防御体系
┌──────────────────────────────────────────────────────────────────┐
│ 纵深防御体系 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ Layer 1: 网络层 │
│ • DDoS 清洗(云厂商高防) │
│ • BGP 黑洞路由 │
│ • SYN Cookie 防护 │
│ │
│ Layer 2: CDN/WAF 层 │
│ • CDN 隐藏源站 IP │
│ • WAF 规则(SQL注入/XSS/CC) │
│ • 频率限制、Bot 识别 │
│ │
│ Layer 3: 网关层 │
│ • Nginx 限流 │
│ • IP 黑白名单 │
│ • 安全响应头 │
│ │
│ Layer 4: 应用层 │
│ • 参数校验/过滤 │
│ • 权限控制、验证码 │
│ • CSRF Token │
│ │
│ Layer 5: 数据层 │
│ • 预编译 SQL、最小权限 │
│ • 敏感数据加密、审计日志 │
│ │
│ Layer 6: 监控层 │
│ • 实时告警、日志分析 │
│ • 异常检测、安全扫描 │
│ │
└──────────────────────────────────────────────────────────────────┘7.2 自动化防护脚本
#!/bin/bash
# auto_defense.sh - 自动化防护脚本
THRESHOLD_QPS=1000
THRESHOLD_ERROR=100
LOG_FILE="/var/log/nginx/access.log"
BANNED_FILE="/etc/nginx/blocked_ips.conf"
BAN_LOG="/var/log/auto_ban.log"
# 获取高频 IP
high_freq=$(tail -10000 $LOG_FILE | awk '{print $1}' | \
sort | uniq -c | sort -rn | \
awk -v t=$THRESHOLD_QPS '$1 > t {print $2}')
# 获取高频错误 IP
high_error=$(tail -10000 $LOG_FILE | awk '$9 >= 400 {print $1}' | \
sort | uniq -c | sort -rn | \
awk -v t=$THRESHOLD_ERROR '$1 > t {print $2}')
# 合并并封禁
attack_ips=$(echo -e "$high_freq\n$high_error" | sort -u | grep -v "^$")
for ip in $attack_ips; do
grep -q "$ip" /etc/nginx/whitelist.conf 2>/dev/null && continue
grep -q "deny $ip" $BANNED_FILE 2>/dev/null && continue
echo "deny $ip;" >> $BANNED_FILE
ipset add blacklist $ip 2>/dev/null
echo "[$(date)] BANNED: $ip" >> $BAN_LOG
done
[ -n "$attack_ips" ] && nginx -s reload八、避坑指南
8.1 安全防护常见坑
| 序号 | 坑点 | 影响 | 预防措施 |
|---|---|---|---|
| 1 | 源站 IP 暴露 | 绕过 CDN 直接打源站 | 服务器只允许 CDN IP 访问 |
| 2 | WAF 规则太严 | 误杀正常用户 | 先观察模式后开启阻断 |
| 3 | 无日志保留策略 | 取证困难 | 日志至少保留 90 天 |
| 4 | 限流只在 Nginx | 应用层无防护 | 多层限流 |
| 5 | 管理后台暴露 | 被爆破 | 管理后台加 IP 白名单 + VPN |
| 6 | API 无鉴权 | 数据泄露 | 所有 API 强制鉴权 |
| 7 | 弱密码 | 被暴力破解 | 强制复杂密码 + 失败锁定 |
| 8 | 未更新组件 | 已知漏洞被利用 | 定期安全扫描 + 及时更新 |
8.2 应急响应避坑
坑1: 限流太狠,正常用户也被限制
解决: 分级限流,攻击 IP 严格限,正常 IP 宽松限
坑2: 封 IP 误杀 NAT 用户
解决: 不封大网段,只封具体 IP;对国内 IP 更谨慎
坑3: 切流量后忘记切回
解决: 设置定时提醒,定期检查 DNS 解析
坑4: 只封 IP 不防 payload
解决: IP 封禁 + WAF 规则 + 应用层校验三层防护
坑5: 日志被攻击者清除
解决: 日志实时推送到远程日志服务器
坑6: 应急时无人协调
解决: 提前制定应急预案,明确各角色职责九、面试要点
Q1: 线上被 DDoS 攻击怎么处理?
回答框架:
- 确认攻击:看 QPS、带宽、连接数是否异常
- 紧急止血:限流 → 封 IP → 切高防
- 分析攻击:攻击类型、IP 来源、目标接口
- 防护加固:WAF 规则、CDN 防护、黑洞路由
- 恢复复盘:逐步解除限制、编写报告、完善预案
Q2: 如何区分 CC 攻击和正常流量突增?
回答要点:
- CC 攻击:单 IP 高频相同 URL、异常 UA、请求间隔固定
- 正常突增:多 IP 分散、不同 URL、正常浏览器 UA
- CC 攻击常针对耗资源接口(搜索、排序)
- 看请求来源分布:CC 攻击 IP 集中,正常流量分散
Q3: SQL 注入怎么防?
回答要点:
- WAF 规则:检测 union select、sleep、benchmark 等
- 预编译 SQL:参数化查询,彻底解决
- 最小权限:数据库用户只授予必要权限
- 输入校验:白名单校验,拒绝特殊字符
- 审计日志:记录所有 SQL 执行
Q4: WAF 规则怎么设计避免误杀?
回答要点:
- 先观察模式(只告警不阻断)
- 分析误报率,调整规则
- 白名单排除已知正常请求
- 分级防护:高置信度阻断,低置信度告警
- 持续迭代:根据误报和漏报优化规则
Q5: 应急响应的优先级是什么?
回答要点:
- 保核心:先保住核心交易链路,牺牲非核心功能
- 先止血:限流封 IP 切流量,快速恢复服务
- 后取证:止血后再分析攻击特征和日志
- 同步通知:第一时间通知相关干系人
- 复盘改进:事后编写报告,完善防护体系
十、总结
线上攻击排查的核心原则:
- 先止血后分析 — 服务可用性第一,分析取证第二
- 多层防护 — 网络、CDN、WAF、Nginx、应用层纵深防御
- 自动化响应 — 自动检测 + 自动封禁 + 自动降级
- 日志是关键 — 没有日志就无法取证,实时推送远程存储
- 预案要提前 — 攻击发生时没有时间研究方案,SOP 必须提前制定
- 定期演练 — 安全演练验证预案有效性,发现防护盲点