被 DDoS 攻击的一夜
被 DDoS 攻击的一夜那是一个普通的周三晚上我正在沙发上刷手机突然收到服务器监控的警报——我的个人博客一个承载了我三年技术笔记和实验记录的小站流量在五分钟内暴涨了 4000 倍。我冲进书房打开电脑看到 Grafana 面板上那条垂直上升的红色曲线像一道刺眼的伤口。接下来四个小时我与那些来自全球各地的恶意流量搏斗直到凌晨三点才勉强让服务恢复。那一夜我真正理解了分布式拒绝服务DDoS攻击的恐怖也学会了防御它的基本功。下面我想用这次实战经历带你从零开始理解 DDoS并给出可运行的防御代码。### 第一步理解 DDoS 的基本原理DDoS 攻击的本质是攻击者利用大量被控制的“僵尸”设备通常是感染了恶意软件的物联网设备、个人电脑或服务器同时向目标服务器发送海量请求耗尽服务器的带宽、CPU、内存或连接表导致正常用户无法访问。最常见的类型有三种-流量型攻击如 UDP Flood直接塞满带宽。-连接型攻击如 SYN Flood占满服务器的半连接队列。-应用层攻击如 HTTP Flood模拟正常浏览器请求消耗应用资源。我那一夜遭遇的是典型的 SYN Flood 和 HTTP Flood 混合攻击——先是几百万个虚假的 TCP 连接请求塞满队列接着大量请求打到我的 Nginx 上。### 第二步手动检测——用 Python 快速识别异常流量在攻击发生后的前十分钟我没有现成工具只能靠脚本判断。以下这段 Python 代码可以实时监控当前服务器上的 TCP 连接状态找出异常来源 IP。这是最基本的“止血”手段。python# detect_ddos.py# 作用监控 TCP 连接状态找出 SYN_RECV半连接数量异常的 IPimport subprocessimport collectionsimport timedef get_connections(): # 使用 netstat 获取所有 TCP 连接只提取状态为 SYN_RECV 的 result subprocess.run([netstat, -nt], capture_outputTrue, textTrue) lines result.stdout.split(\n) syn_recv_ips [] for line in lines: parts line.split() # 格式示例tcp 0 0 1.2.3.4:80 5.6.7.8:12345 SYN_RECV if len(parts) 6 and parts[5] SYN_RECV: # 提取对方 IP去掉端口 remote_ip parts[4].split(:)[0] syn_recv_ips.append(remote_ip) return syn_recv_ipsdef analyze(): ip_counter collections.Counter(get_connections()) print(当前 SYN_RECV 连接数 Top 10) for ip, count in ip_counter.most_common(10): if count 100: # 如果单个 IP 超过 100 个半连接可疑 print(f⚠️ 可疑 IP {ip} 半连接数: {count})# 每 5 秒检测一次持续 10 次for _ in range(10): analyze() time.sleep(5)运行这段脚本后我立刻看到来自三个 IP 的 SYN_RECV 连接数突破了 500确认了攻击源。但手动检测只是第一步真正的防御需要自动化响应。### 第三步进阶防御——用 iptables 自动封禁恶意 IP检测到攻击源后最直接的手段是使用防火墙封禁 IP。但手动封禁太慢而且攻击者会不断换 IP。我写了一个更完善的 Python 脚本它持续监控连接数一旦发现某个 IP 的半连接数超过阈值自动调用 iptables 封禁该 IP 一小时。这是那次“战斗”中我最依赖的工具。python# auto_block.py# 作用自动检测 SYN Flood 攻击并封禁恶意 IPimport subprocessimport timeimport re# 封禁阈值单个 IP 的 SYN_RECV 连接数超过 150 即封禁THRESHOLD 150# 封禁时长秒3600 秒 1 小时BLOCK_TIME 3600def get_suspicious_ips(): # 获取所有 SYN_RECV 连接统计 IP 出现次数 result subprocess.run([netstat, -nt], capture_outputTrue, textTrue) ip_count {} for line in result.stdout.split(\n): if SYN_RECV in line: parts line.split() if len(parts) 5: ip parts[4].split(:)[0] # 提取对方 IP ip_count[ip] ip_count.get(ip, 0) 1 # 返回超过阈值的 IP 列表 return [ip for ip, count in ip_count.items() if count THRESHOLD]def block_ip(ip): # 使用 iptables 封禁 IP需要 root 权限 print(f 正在封禁恶意 IP: {ip}) subprocess.run([iptables, -A, INPUT, -s, ip, -j, DROP]) # 设置一个定时解封任务用 at 命令或者这里简单演示为 sleep 后解封 # 注意实际生产环境中建议用 cron 或 systemd timer subprocess.run([bash, -c, f(sleep {BLOCK_TIME} iptables -D INPUT -s {ip} -j DROP) ]) print(f✅ 已封禁 {ip}{BLOCK_TIME} 秒后自动解封)def main(): print(开始监控 SYN Flood 攻击...) while True: suspicious_ips get_suspicious_ips() for ip in suspicious_ips: block_ip(ip) time.sleep(2) # 每 2 秒扫描一次if __name__ __main__: # 需要 root 权限运行 main()这个脚本救了我的服务器。当攻击流量再次涌来时它自动封禁了 40 多个 IP将 SYN_RECV 数量从 2000 降到了 50 以下。但封禁 IP 只是治标攻击者很快就换上了新的 IP。### 第四步应用层防御——识别 HTTP FloodSYN Flood 被压制后攻击者转向了 HTTP Flood——模拟真实浏览器请求我的主页和 API。这时单纯的 IP 封禁已经不够因为攻击者会用分布式的 IP。我需要识别请求的特征比如 User-Agent 是否是常见的浏览器、请求频率是否异常。我写了一个 Nginx 层级的防御配置也可以直接用 Python 写一个简单的反向代理但更实用的是用 Python 分析访问日志找出高频请求的 IP 并临时限速。下面这段代码展示了一个简单的“速率限制”防御逻辑python# rate_limit.py# 作用从 Nginx 访问日志中检测高频请求并生成防火墙规则import refrom collections import defaultdict# 读取 Nginx 访问日志假设格式为 combinedlog_file /var/log/nginx/access.log# 每 10 秒内同一 IP 允许的最大请求数MAX_REQUESTS 100def parse_log(file_path): ip_requests defaultdict(int) # 常见日志行示例192.168.1.1 - - [10/Oct/2024:13:55:36 0000] GET / HTTP/1.1 200 123 pattern r^(\d\.\d\.\d\.\d).*GET.* (\d{3}) with open(file_path, r) as f: for line in f: match re.match(pattern, line) if match: ip match.group(1) status match.group(2) # 只统计 200 请求避免误伤 4xx/5xx if status 200: ip_requests[ip] 1 return ip_requestsdef generate_block_rules(): ip_counts parse_log(log_file) print(高频请求 IP 列表) for ip, count in sorted(ip_counts.items(), keylambda x: -x[1])[:20]: if count MAX_REQUESTS: print(f⚠️ {ip} 在 10 秒内请求了 {count} 次建议限速) # 这里可以调用 iptables 或 Nginx 的 limit_req 模块 # 例如subprocess.run([iptables, -A, INPUT, -p, tcp, --dport, 80, -s, ip, -j, DROP])if __name__ __main__: generate_block_rules()这段代码让我看到了攻击的“真面目”——有 20 多个 IP 在 10 秒内请求了上千次明显是脚本行为。我把这些 IP 加入黑名单后HTTP Flood 也趋于平静。### 第五步基础设施层面的终极防线那晚的最后一小时我意识到个人服务器的防御能力有限。真正的 DDoS 防御需要基础设施层面的支持比如-CDN 和云防护将流量分流到 Cloudflare 或阿里云盾隐藏源站 IP。-负载均衡分散压力到多台服务器。-带宽冗余购买远高于正常需求的带宽。这些不是代码能解决的但为了完整性我分享一个简单的 Nginx 配置用于限制单个 IP 的并发连接数和请求速率这是应用层防御的常用做法nginx# nginx.conf 片段http { # 定义限速区域每个 IP 每分钟最多 60 个请求 limit_req_zone $binary_remote_addr zonemylimit:10m rate1r/s; server { listen 80; server_name myblog.com; # 限制并发连接数 limit_conn addr 10; location / { # 应用限速规则 limit_req zonemylimit burst20 nodelay; proxy_pass http://backend; } }}### 总结那一夜我从惊慌失措到沉着应对最终用 Python 脚本和 iptables 守住了服务器。DDoS 攻击虽然可怕但并非无解。对于个人站长或小企业掌握基本的检测和封禁技能如本文的代码可以应对中小规模攻击但对于大规模攻击必须依赖专业的云防护服务。记住不要试图用单点防御去对抗分布式攻击——那就像用一块石头去挡洪水。这次经历教会我的最重要一课是提前做好防御准备比攻击发生时再想办法有效一百倍。现在我的服务器已经配置了自动限速、IP 黑名单和 CDN 防护再遇到类似情况我可以安心睡个好觉了。