被 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_output=True, text=True) 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_output=True, text=True) 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 访问日志(假设格式为 combined)log_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(), key=lambda 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 zone=mylimit:10m rate=1r/s; server { listen 80; server_name myblog.com; # 限制并发连接数 limit_conn addr 10; location / { # 应用限速规则 limit_req zone=mylimit burst=20 nodelay; proxy_pass http://backend; } }}### 总结那一夜,我从惊慌失措到沉着应对,最终用 Python 脚本和 iptables 守住了服务器。DDoS 攻击虽然可怕,但并非无解。对于个人站长或小企业,掌握基本的检测和封禁技能(如本文的代码)可以应对中小规模攻击;但对于大规模攻击,必须依赖专业的云防护服务。记住,不要试图用单点防御去对抗分布式攻击——那就像用一块石头去挡洪水。这次经历教会我的最重要一课是:提前做好防御准备,比攻击发生时再想办法有效一百倍。现在,我的服务器已经配置了自动限速、IP 黑名单和 CDN 防护,再遇到类似情况,我可以安心睡个好觉了。