CC防护的本质不是买设备或者装面板,而是在攻击流量抵达业务逻辑之前,用最低成本识别并丢弃它。很多人以为基础CC防护需要WAF、需要Nginx Plus、需要Lua脚本,实际上用Nginx原生模块和几行配置就能挡掉相当一部分自动化攻击。下面直接讲配置逻辑和可落地的代码。

先理解CC攻击的流量特征

CC攻击不同于带宽型DDoS,它通常表现为大量正常请求打向同一个或几个URL,请求本身合法,但频率异常高。攻击者往往固定User-Agent、固定IP段、固定请求路径,甚至不带Cookie。因此基础防护的核心思路是:限制单IP请求频率、识别异常客户端特征、对可疑流量直接返回444或503。

Nginx提供了两个关键模块:ngx_http_limit_req_module和ngx_http_limit_conn_module。前者限制请求速率,后者限制并发连接数。配合map和if判断,可以做到不依赖第三方组件的基础CC拦截。

核心配置一:限制单IP请求速率

在Nginx的http块中定义限速规则:

http {
    limit_req_zone $binary_remote_addr zone=cc_zone:10m rate=20r/s;
    limit_req_status 444;

    server {
        listen 80;
        server_name example.com;

        location / {
            limit_req zone=cc_zone burst=30 nodelay;
            proxy_pass http://backend;
        }
    }
}

这里rate=20r/s表示每个IP每秒最多20个请求。burst=30是允许的突发队列长度,nodelay让突发请求立即处理而不是排队。超过限制的请求直接返回444,Nginx会直接断开连接,不返回任何内容,减少响应开销。10m的共享内存大约可以存储16万个IP的状态,中小站点足够使用。

这个配置的独到之处在于limit_req_status设置成444。很多教程用503,但503会返回错误页,攻击者依然能消耗服务器资源。444是Nginx自定义的非标准状态码,连接直接关闭,攻击端往往收不到完整响应,能有效降低攻击工具的持续性。

核心配置二:限制单IP并发连接数

有些CC工具不是高频请求,而是慢速建立大量连接占用资源。此时需要限制并发:

http {
    limit_conn_zone $binary_remote_addr zone=conn_zone:10m;

    server {
        location / {
            limit_conn conn_zone 10;
            limit_conn_status 503;
            proxy_pass http://backend;
        }
    }
}

limit_conn conn_zone 10表示同一IP同时最多10个连接。超过后返回503。这个参数需要根据业务调整,如果是API服务可以设小一点,如果是网页浏览可以适当放大。并发限制和速率限制配合使用,能覆盖大多数基础CC场景。

核心配置三:识别并拦截异常User-Agent

大量CC工具使用默认UA或者空UA,通过map可以快速拦截:

http {
    map $http_user_agent $is_bad_ua {
        default 0;
        "" 1;
        "~*python" 1;
        "~*curl" 1;
        "~*wget" 1;
        "~*scrapy" 1;
        "~*httpclient" 1;
        "~*okhttp" 1;
        "~*go-http-client" 1;
        "~*libwww-perl" 1;
    }

    server {
        location / {
            if ($is_bad_ua = 1) {
                return 444;
            }
            proxy_pass http://backend;
        }
    }
}

这个配置将空UA和常见编程语言HTTP库的UA特征直接标记为恶意。正常浏览器不会使用这些UA访问网站。需要注意的是,某些合法RSS阅读器或监控工具可能使用curl,如果业务依赖这些来源,需要单独加白名单IP或者调整规则。

核心配置四:限制特定URL的访问频率

CC攻击通常集中在登录、搜索、API接口等消耗资源的URL。针对这些路径单独设置更严格的限速:

http {
    limit_req_zone $binary_remote_addr zone=login_zone:10m rate=5r/m;
    limit_req_zone $binary_remote_addr zone=api_zone:10m rate=50r/s;

    server {
        location /login {
            limit_req zone=login_zone burst=3 nodelay;
            limit_req_status 444;
            proxy_pass http://backend;
        }

        location /api/ {
            limit_req zone=api_zone burst=20 nodelay;
            limit_req_status 444;
            proxy_pass http://backend;
        }
    }
}

登录接口限制为每分钟5次,API接口限制为每秒50次。这种差异化配置比全局一刀切更合理,既保护了高消耗接口,又不影响普通页面浏览。攻击者如果打登录接口,很快就会被封禁。

核心配置五:利用Cookie识别真实用户

CC攻击工具往往不处理Cookie,或者每次请求都不携带Cookie。可以通过判断Cookie是否存在来区分真实用户和自动化工具:

http {
    map $http_cookie $is_real_user {
        default 0;
        "~*sessionid" 1;
    }

    server {
        location / {
            if ($is_real_user = 0) {
                limit_req zone=cc_zone burst=5 nodelay;
            }
            proxy_pass http://backend;
        }
    }
}

这个配置的逻辑是:没有携带sessionid Cookie的请求,限速更严格。正常用户登录后浏览器会带上Cookie,而攻击脚本通常不会维护Cookie。不过这个方案需要业务系统确实设置了名为sessionid的Cookie,实际部署时要根据真实Cookie名称调整。

核心配置六:自动封禁高频攻击IP

单纯的限速只能丢弃请求,攻击IP依然会持续发送流量。更主动的做法是识别高频IP并直接封禁一段时间。Nginx没有内置这个功能,但可以通过日志分析和iptables配合实现。这里给一个轻量级方案:

# 定义自定义日志格式,记录请求频率
http {
    log_format cc_format '$remote_addr $request_uri $status $time_local';
    
    server {
        location / {
            limit_req zone=cc_zone burst=30 nodelay;
            limit_req_status 444;
            access_log /var/log/nginx/cc.log cc_format;
            proxy_pass http://backend;
        }
    }
}

然后配合一个简单的shell脚本定时分析日志:

#!/bin/bash
# 每分钟执行一次,封禁请求超过200次的IP
tail -n 5000 /var/log/nginx/cc.log | awk '{print $1}' | sort | uniq -c | sort -rn | while read count ip; do
    if [ $count -gt 200 ]; then
        iptables -I INPUT -s $ip -j DROP
        echo "$(date) blocked $ip with $count requests" >> /var/log/nginx/block.log
    fi
done

这个脚本统计最近5000条日志中每个IP的请求次数,超过200次就直接用iptables封禁。封禁发生在网络层,比Nginx层更彻底,CPU消耗更低。不过要注意设置封禁有效期,否则可能误伤NAT出口的合法用户。可以配合at命令或者定时任务定期清理iptables规则。

完整配置示例

将上述所有策略整合到一个完整的Nginx配置中:

http {
    # 限速区域定义
    limit_req_zone $binary_remote_addr zone=global_zone:10m rate=30r/s;
    limit_req_zone $binary_remote_addr zone=login_zone:10m rate=5r/m;
    limit_req_zone $binary_remote_addr zone=api_zone:10m rate=50r/s;
    limit_conn_zone $binary_remote_addr zone=conn_zone:10m;

    # 恶意UA识别
    map $http_user_agent $is_bad_ua {
        default 0;
        "" 1;
        "~*python" 1;
        "~*curl" 1;
        "~*wget" 1;
        "~*scrapy" 1;
        "~*httpclient" 1;
        "~*okhttp" 1;
        "~*go-http-client" 1;
    }

    # 真实用户识别
    map $http_cookie $is_real_user {
        default 0;
        "~*sessionid" 1;
    }

    server {
        listen 80;
        server_name example.com;
        access_log /var/log/nginx/cc.log;

        # 全局并发限制
        limit_conn conn_zone 15;
        limit_conn_status 503;

        # 拦截恶意UA
        if ($is_bad_ua = 1) {
            return 444;
        }

        location / {
            limit_req zone=global_zone burst=40 nodelay;
            limit_req_status 444;
            proxy_pass http://backend;
        }

        location /login {
            limit_req zone=login_zone burst=3 nodelay;
            limit_req_status 444;
            proxy_pass http://backend;
        }

        location /api/ {
            limit_req zone=api_zone burst=20 nodelay;
            limit_req_status 444;
            proxy_pass http://backend;
        }
    }
}

这套配置不需要安装任何额外软件,纯Nginx原生能力就能实现基础CC防护。实际部署时需要注意几个细节:limit_req_zone的rate要根据业务真实流量调整,设置过低会误伤正常用户;burst参数不宜过大,否则攻击者可以利用突发队列绕过限制;444状态码虽然有效,但某些监控系统可能无法识别,需要调整告警规则。

对于更复杂的CC攻击,比如分布式代理池攻击、慢速攻击、TLS层攻击,这套基础配置就不够用了。但作为第一道防线,它能在攻击初期就过滤掉大量垃圾流量,为上层防护争取时间。而且配置简单、资源消耗极低,任何Nginx服务器都可以快速部署。