很多中小企业的技术负责人第一次遭遇CC攻击时,往往是在业务高峰期服务器突然卡死,CPU跑满100%,正常用户完全无法访问。打开服务器日志一看,全是某个接口或页面的重复请求,每秒几百甚至上千次,来源IP分散在全国各地,封都封不过来。这就是典型的CC攻击,它不靠大流量洪水,而是模拟真实用户行为,专挑资源消耗大的页面反复请求,用极低的成本就能拖垮一台配置不错的服务器。
对于预算有限的中小企业来说,上高防服务器或采购商业防护产品往往成本过高,动辄几万一年。但实际上,只要理解CC攻击的核心原理,完全可以用开源工具和云服务商的基础功能,搭建一套低成本甚至零成本的防护体系。下面直接讲完整的部署流程,从架构选择到具体配置,每一步都可落地执行。
理解CC攻击的三个核心特征在动手部署之前,必须先搞清楚CC攻击到底在攻击什么。很多人误以为CC攻击就是大量请求,其实不然。CC攻击的精髓在于“精准消耗”,攻击者会挑选那些需要数据库查询、复杂计算、文件读取的动态页面,比如搜索接口、登录接口、购物车提交页面。一个简单的静态HTML页面,攻击者发一万次请求也消耗不了多少资源,但一个未做优化的搜索接口,可能一次请求就执行了全表扫描,单次耗时几百毫秒,一百个并发就能把数据库连接池打满。
第二个特征是攻击流量高度模拟正常用户。攻击请求的User-Agent、Referer、Cookie都会伪装得很正常,甚至有些高级攻击会携带真实的浏览器指纹,让单纯的请求头校验完全失效。第三个特征是攻击源IP高度分散,攻击者通常使用代理池或僵尸网络,每个IP只发少量请求,传统的IP频率限制很难精准拦截。
理解了这三个特征,防护思路就很清晰了:第一,保护资源消耗大的动态接口,降低单次请求的服务器开销;第二,在应用层识别异常行为模式,而不是单纯看IP;第三,分层过滤,把恶意请求挡在离核心业务越远越好的地方。
第一层防护:Web服务器层面的连接限制CC攻击本质上要建立大量TCP连接,所以第一道防线应该设在Web服务器层面。Nginx是目前中小企业使用最广泛的Web服务器,它的两个核心模块就能过滤掉大部分低级别的CC攻击。
首先是limit_conn模块,用来限制单个IP的并发连接数。很多CC攻击为了快速消耗资源,会同时发起大量连接。在nginx.conf的http段或server段添加以下配置:
http {
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
server {
listen 80;
server_name example.com;
location / {
limit_conn conn_limit 10;
limit_conn_status 503;
}
}
}
这段配置的含义是,每个IP地址最多同时保持10个连接,超出后直接返回503状态码。10m的内存空间可以存储大约16万个IP的连接状态,对于大多数中小企业足够了。这里要注意,limit_conn限制的是同时存在的连接数,对于短连接场景效果有限,但对于长连接攻击或慢速攻击非常有效。
其次是limit_req模块,用来限制请求速率。这是对抗CC攻击最直接的武器,配置如下:
http {
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
server {
location / {
limit_req zone=req_limit burst=20 nodelay;
limit_req_status 503;
}
}
}
rate=10r/s表示每秒最多处理10个请求,burst=20表示允许20个请求的突发队列,nodelay表示超出burst的请求立即返回503而不排队。这个配置对单个IP的请求频率做了硬限制,能有效拦截高频攻击。但这里有个坑,如果攻击者使用了大量代理IP,每个IP的请求频率都在阈值以下,limit_req就失效了。所以这个方案只能作为第一层粗筛,不能作为唯一的防护手段。
第二层防护:应用层的智能识别与验证当攻击请求绕过了Nginx的频率限制,到达应用层时,就需要更精细的识别机制了。这里不推荐直接在业务代码里写防护逻辑,会严重耦合,维护困难。更优雅的做法是在Nginx层面通过Lua脚本做前置处理,OpenResty就是为此而生的。
OpenResty集成了LuaJIT,可以在Nginx的请求处理阶段执行自定义脚本,性能损耗极小。安装OpenResty后,在nginx.conf中引入Lua脚本,实现一个简单的Cookie验证机制:
server {
listen 80;
server_name example.com;
location / {
access_by_lua_block {
local token = ngx.var.cookie_token
if not token then
-- 生成一个简单的token,实际使用中应该用更安全的算法
local new_token = ngx.md5(ngx.var.remote_addr .. ngx.var.http_user_agent .. ngx.time())
ngx.header['Set-Cookie'] = 'token=' .. new_token .. '; path=/; HttpOnly'
-- 302重定向到验证页面或直接返回验证码
return ngx.redirect('/verify.html')
end
-- 验证token有效性
local valid = ngx.md5(ngx.var.remote_addr .. ngx.var.http_user_agent .. ngx.time() - 3600)
if token ~= valid and token ~= ngx.md5(ngx.var.remote_addr .. ngx.var.http_user_agent .. ngx.time()) then
return ngx.exit(403)
end
}
proxy_pass http://backend;
}
}
这个脚本的原理很简单:首次访问的请求没有Cookie,会被重定向到验证页面,验证通过后种下Cookie,后续请求携带有效Cookie才能访问真实业务。CC攻击工具通常不会执行JavaScript,也就无法完成Cookie验证,攻击流量在这一层就被拦截了。
但这种方式有个缺点,会误伤搜索引擎爬虫和部分API调用。解决方法是设置白名单,对特定User-Agent或特定路径放行。更进阶的做法是使用浏览器指纹验证,比如Cloudflare的Turnstile或自建的无感验证系统,用户体验更好,但实现复杂度也更高。
第三层防护:缓存与静态化策略很多中小企业被CC攻击打垮,根本原因不是防护不到位,而是服务器性能本身就差,稍微有点并发就撑不住。与其花精力做复杂的防护,不如先把业务层面的性能优化做好,让服务器能承受更高的并发。
最有效的优化手段就是缓存。对于不经常变动的页面,比如产品详情页、文章内容页,直接在Nginx层面做全页缓存:
http {
proxy_cache_path /tmp/nginx_cache levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=60m use_temp_path=off;
server {
location / {
proxy_cache my_cache;
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_lock on;
proxy_pass http://backend;
}
}
}
proxy_cache_lock on这个参数很重要,它确保同一个资源同时只有一个请求穿透到后端,其他请求等待缓存结果,能有效防止缓存击穿。对于登录用户看到的个性化内容,可以用ESI或AJAX异步加载的方式处理,主体内容仍然走缓存。
对于搜索接口这类无法缓存的动态请求,必须做查询优化和结果限制。比如强制分页大小不超过20条,使用Elasticsearch代替MySQL做全文检索,对热门搜索词做预计算缓存。这些优化措施本身就能让单次请求的资源消耗降低一个数量级,攻击者需要付出更高的成本才能造成同样的伤害。
第四层防护:CDN与云服务商的免费能力到了这一层,如果攻击流量仍然很大,就需要借助外部力量了。国内主流的CDN服务商,比如阿里云CDN、腾讯云CDN、七牛云等,都提供基础的DDoS和CC防护能力,而且有免费额度。对于日均PV在几万以下的中小网站,免费额度通常够用。
以阿里云CDN为例,接入后默认就带有CC防护功能,能自动识别并拦截高频攻击请求。配置非常简单,只需要把域名的DNS解析指向CDN提供的CNAME地址,然后在CDN控制台开启CC防护,设置一个合理的阈值即可。CDN节点遍布全国,攻击流量在边缘节点就被分散和过滤了,根本到不了源站。
这里有个关键细节:CDN的回源IP必须做白名单限制,在源站服务器的安全组或防火墙中,只允许CDN的回源IP段访问80和443端口,其他所有IP直接拒绝。这样即使攻击者拿到了源站的真实IP,也无法直接攻击。具体操作是在阿里云安全组规则中,入方向只放行CDN官方公布的回源IP段,拒绝所有其他来源。
另外,云服务商的WAF产品也值得考虑。阿里云WAF和腾讯云WAF都有基础版,每月几十块钱,提供SQL注入、XSS、CC攻击等常见威胁的防护能力。对于技术团队薄弱的中小企业,花小钱买个省心是划算的。
第五层防护:日志监控与自动化响应防护体系搭建完成后,还需要持续的监控和优化。CC攻击的手法在不断演变,今天的规则可能明天就失效了。建议搭建一套简单的日志分析系统,实时监控异常流量。
最简单的方案是用Filebeat收集Nginx日志,输出到Elasticsearch,用Kibana做可视化分析。重点监控几个指标:每秒请求数、状态码分布、请求耗时分布、单个IP的请求频率、单个URL的访问量。当发现某个URL的访问量突然飙升,或者某个IP段的请求占比异常,就要及时调整防护策略。
更进一步,可以写一个定时脚本,每分钟分析一次日志,自动提取高频IP并加入iptables黑名单:
#!/bin/bash
# 分析Nginx日志,封禁1分钟内请求超过100次的IP
tail -n 10000 /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | while read count ip; do
if [ $count -gt 100 ]; then
iptables -I INPUT -s $ip -j DROP
echo "$(date) blocked $ip with $count requests" >> /var/log/block.log
fi
done
这个脚本比较粗糙,实际使用中需要加入白名单机制,避免误封CDN回源IP和搜索引擎爬虫。更完善的方案是结合fail2ban,它支持自定义过滤规则和自动解封时间,能减少人工干预。
实战中的经验与注意事项整个部署流程走下来,有几个容易踩坑的地方需要特别注意。第一,Nginx的limit_req和limit_conn配置不要设得过于严格,否则会误伤正常用户,尤其是企业内网用户可能共享同一个出口IP,限制太死会导致集体无法访问。建议先观察正常业务高峰期的请求量,阈值设置为峰值的2到3倍。
第二,Cookie验证方案对用户体验有影响,首次访问会有跳转,对于追求极致加载速度的网站要谨慎使用。可以考虑只在检测到异常流量时才开启验证,平时关闭。判断异常流量的依据可以是服务器负载、请求队列长度等系统指标。
第三,多层防护的优先级要理清楚。CDN是第一道防线,Nginx频率限制是第二道,应用层验证是第三道,缓存优化是兜底。每一层都要记录日志,方便事后分析攻击特征。不要指望单靠某一层就能挡住所有攻击,分层防御、纵深配置才是正道。
第四,成本控制方面,CDN的免费额度和云WAF的基础版已经能满足大部分场景。如果业务规模增长到需要更高等级的防护,再考虑升级商业方案。对于日均攻击量在几万次以下的网站,本文描述的方案完全免费或月成本不超过一百元,性价比极高。
最后强调一点,CC防护不是一劳永逸的事情。攻击者会不断研究新的绕过手法,防护策略也要持续迭代。建议每季度做一次压力测试,用ab或wrk工具模拟并发请求,检验防护体系的有效性。只有真正压测过,才知道自己的系统能扛住多大体量的攻击。
