静态网站常被误认为无需担心CC攻击,实际上静态页面虽然不查数据库、不执行复杂后端逻辑,但HTTP请求到达服务器时,TCP握手、TLS协商、Web服务器解析请求头、读取磁盘或缓存文件、构造响应、写访问日志,这一整条链路依然要消耗CPU、内存、带宽和文件描述符。攻击者用大量看似正常的GET请求打向首页或某个静态资源,请求量一旦超过服务器单机处理能力,正常用户就打不开页面。静态站点的防御难点在于攻击请求和正常请求在协议层面几乎没有区别,不能靠简单的URI特征拦截,而传统WAF或CDN方案对个人站长、小型企业来说又太重、太贵、配置复杂。轻量化防御的核心思路是:在不引入重型安全设备的前提下,利用Web服务器自身能力、操作系统内核参数和极简自动化脚本,把攻击流量在到达应用之前尽量拦截或降级。

先厘清静态站CC攻击的三种主要形态

第一种是直接对源站IP发起海量请求,攻击者绕过DNS直接打服务器,这种最直接也最难靠CDN解决。第二种是打固定URL,比如首页、某个大体积JS或图片,利用静态文件读取和传输消耗带宽。第三种是慢速攻击,比如慢速读响应、慢速发请求头,单个连接占用时间长,少量连接就能拖垮默认配置的Web服务器。很多站长一遇到CPU飙高就以为是CC,其实静态站遇到突发流量和CC在现象上很像,但处理方式完全不同。突发流量是短时间正常用户集中访问,CC是攻击者用工具维持的高并发。判断依据可以看连接数、请求速率、UA分布、单IP请求频率、是否集中在少数URL、是否有规律的时间间隔。日志里如果出现大量同一秒内来自不同IP但UA相同、请求路径相同的记录,基本可以判定为CC。

轻量化防御的第一层:Web服务器参数调优

Nginx是静态站最常用的Web服务器,默认配置在攻击场景下非常脆弱。worker_processes设置为auto,worker_connections根据内存调整,一般单机可以开到10000以上,但更重要的是限制单个IP的连接数和请求速率。limit_conn_zone和limit_req_zone是Nginx自带的两道闸门,前者限制单IP并发连接数,后者限制单IP请求速率。静态站通常不需要单IP保持太多并发,limit_conn设置10到20足够,limit_req设置每秒5到10个请求,突发允许10到20个。这两个模块在Nginx编译时默认包含,配置成本几乎为零,但对CC攻击的压制效果非常明显。需要注意的是,limit_req的burst参数要配合nodelay使用,否则请求会被排队,反而造成连接堆积。另外,静态资源可以开启sendfile、tcp_nopush、gzip_static,减少磁盘IO和传输体积,但gzip在CPU紧张时可以关闭,因为压缩本身也消耗CPU。

第二层:内核参数与TCP层防护

CC攻击本质上是大量TCP连接和HTTP请求,操作系统层面的参数直接影响服务器能承受的连接压力。net.core.somaxconn、net.ipv4.tcp_max_syn_backlog、net.ipv4.tcp_syncookies这三个参数要调大或开启。syncookies开启后,SYN Flood类攻击很难消耗半连接队列。net.ipv4.tcp_tw_reuse和tcp_fin_timeout调小可以加快TIME_WAIT状态回收,释放端口和内存。对于静态站,tcp_keepalive_time可以适当缩短,让空闲连接尽快释放。还有一个容易被忽略的参数是net.ipv4.ip_local_port_range,如果源站对外发起请求较多,端口范围要扩大。这些参数通过sysctl.conf持久化,重启不丢失。内核调优不需要额外软件,是纯配置层面的轻量方案,但对连接型CC攻击的防御能力提升是实打实的。

第三层:基于Nginx的智能封禁脚本

单纯靠固定阈值限速会误伤正常用户,比如企业内网多个用户共享一个出口IP,请求频率天然偏高。更合理的做法是动态分析日志,把明显异常的IP临时加入黑名单。一个轻量Shell脚本配合crontab每分钟执行一次即可实现。脚本逻辑是:解析Nginx access_log,统计过去一分钟内每个IP的请求数,超过阈值(比如300次/分钟)且请求集中在少数URL的IP,用iptables或Nginx deny指令封禁。封禁方式上,iptables封IP对CPU消耗更小,因为包在进入用户态之前就被丢弃,但需要root权限且要小心误封。Nginx deny指令更灵活,可以只封特定URI的访问,但请求仍会到达Nginx进程。脚本里要加白名单机制,把搜索引擎蜘蛛的IP段、自己常用的管理IP排除。封禁时间可以设置阶梯式,第一次封10分钟,解封后再次触发封1小时,第三次封24小时。这种脚本实现简单,几十行代码就能跑起来,对个人站长来说是完全可控的轻量方案。

#!/bin/bash
# 简易CC封禁脚本:统计Nginx日志中单IP每分钟请求数
LOG=/var/log/nginx/access.log
THRESHOLD=300
WHITELIST="127.0.0.1 192.168.1.0/24"
BLOCKLIST=/tmp/cc_blocked_ips.txt

awk '{print $1}' $LOG | sort | uniq -c | sort -rn | while read count ip; do
  if [ $count -gt $THRESHOLD ]; then
    if ! echo "$WHITELIST" | grep -q "$ip"; then
      if ! grep -q "$ip" $BLOCKLIST 2>/dev/null; then
        iptables -I INPUT -s $ip -j DROP
        echo "$ip $(date +%s)" >> $BLOCKLIST
      fi
    fi
  fi
done

这个脚本只是基础版,实际使用时要加上日志轮转处理,因为Nginx日志切割后文件名会变,脚本要读取当前有效的日志文件。另外iptables规则重启后会丢失,需要配合iptables-save持久化,或者在脚本启动时重新加载封禁列表。封禁列表本身也要定期清理,避免无限增长。

第四层:利用Nginx的map和geo模块做轻量风控

Nginx的map模块可以在不引入外部程序的情况下,根据请求特征动态设置变量,配合limit_req实现更精细的控制。比如把UA为空的请求、UA包含明显攻击工具特征的请求、请求路径包含特定后缀的请求,映射到更高的限制等级。geo模块可以根据IP段设置不同变量,把海外IP或已知恶意IP段标记为高风险。这两个模块都是Nginx官方自带,配置简单,性能损耗极低。具体做法是定义一个map,把空UA映射为1,正常UA映射为0,然后在limit_req指令里根据这个变量选择不同的限速区域。这样正常浏览器请求不受影响,而攻击工具的请求会被严格限制。对于静态站来说,正常用户一定有浏览器UA,空UA或异常UA的请求大概率是脚本发起的,直接拒绝也不会有太大误伤。

第五层:静态资源访问频率的差异化控制

首页HTML和关键CSS、JS文件是攻击者最常打的目标,因为这些文件小、请求处理快,攻击者可以在短时间内发出大量请求。而图片、视频等大文件攻击成本高,攻击者一般不会用CC打大文件。所以防御策略上,对小文件设置更严格的限速,对大文件设置相对宽松的限速。Nginx里可以用location匹配不同文件类型,分别配置limit_req。比如对.html、.css、.js文件限制单IP每秒3个请求,对.jpg、.png、.mp4限制单IP每秒10个请求。这种差异化控制能有效压制针对小文件的CC攻击,同时不影响正常用户浏览页面时加载多个静态资源。更进一步,可以把首页HTML做内存缓存,用open_file_cache和proxy_cache(如果前面有反向代理)减少磁盘读取,提升单机抗压能力。

第六层:DNS层面的轻量分流

如果源站IP已经暴露,攻击者绕过DNS直接打IP,那DNS层面的防御就失效了。但很多CC攻击还是通过域名发起的,这时候可以利用DNS服务商提供的免费或低成本防护功能。把域名的NS接入支持基础防护的DNS服务,开启代理模式,让流量先经过一层轻量过滤再回源。这种方式不需要自己部署任何软件,配置成本极低,对小型静态站来说是最省事的方案。但要注意,如果攻击者已经拿到源站真实IP,DNS防护形同虚设。所以源站IP的保密很重要,历史上因为邮件头泄露、子域名解析、证书透明日志等原因暴露源站IP的案例很多。轻量方案下,至少要做到源站不直接对外提供服务,只允许防护节点的IP回源,用防火墙限制其他来源的80和443端口访问。

第七层:日志分析与自动化告警

防御不是一次性配置完就结束了,攻击者会不断调整策略,防御方也要持续观察日志。轻量方案不需要上ELK这种重型日志平台,用GoAccess、AWStats这类轻量工具就能分析Nginx日志,生成可视化报表。配合一个简单的Shell脚本,当检测到请求量突增、错误率升高、单IP请求异常时,发送邮件或消息通知站长。告警阈值要设置合理,避免正常流量波动频繁触发。日志分析的重点指标包括:每秒请求数、独立IP数、单IP平均请求数、404比例、非200响应比例、请求路径分布。如果发现某个URL的请求量突然暴增,而该URL平时访问量很低,很可能是攻击者换了目标,需要及时调整限速规则或临时封禁。

轻量化方案的实际效果边界

这套方案能防住大部分脚本小子和中小规模CC攻击,但面对分布式肉鸡组成的大规模攻击、带宽耗尽型攻击、或者针对TLS握手阶段的攻击,单机层面的优化和封禁就力不从心了。轻量化的本质是在有限资源下做最大程度的优化,而不是替代专业防护。如果攻击流量超过服务器带宽上限,任何本地策略都无济于事,因为数据包已经把网卡打满了。这时候需要的是上游运营商清洗或专业高防服务,但那已经超出轻量化的范畴。站长在评估防御方案时,要清楚自己的服务器带宽、CPU核心数、内存大小,根据实际资源设定合理的限速阈值和封禁策略。阈值设得太低会误伤正常用户,设得太高又起不到防护作用,这个平衡需要根据自己站点的正常流量基线来调整。

配置落地时的关键细节

Nginx的limit_req和limit_conn是基于共享内存的,配置在http块里定义zone,在server或location块里引用。zone的大小要足够容纳所有活跃IP的状态,一般1M可以存约16000个IP的状态,静态站用10M的zone基本够用。limit_req的rate参数是每秒允许的请求数,burst是突发队列长度,nodelay表示超出rate但未超出burst的请求立即处理而不是排队。对于静态站,建议对HTML文件设置rate=2r/s burst=5 nodelay,对图片等大文件设置rate=5r/s burst=10 nodelay。limit_conn的并发连接数限制对静态站来说,单IP并发10个连接已经足够,因为浏览器对同一域名的并发连接数本身就有上限。配置完成后要用ab或wrk工具做压测,验证限速是否生效,以及正常浏览是否受影响。压测时注意从不同IP发起请求,模拟真实用户行为,避免自己触发封禁。

静态网站CC攻击防御的轻量化解决方案,核心是把Web服务器、操作系统、脚本自动化这三层的能力用足,用最小的成本实现最大的防护效果。不需要购买昂贵的硬件设备,不需要引入复杂的软件栈,只需要理解攻击原理,合理配置现有资源,就能让一个静态站点在中小规模CC攻击下保持稳定可用。这套方案的维护成本也很低,日常只需要观察日志、调整阈值、更新封禁列表,对个人站长和小型团队来说完全可控。