Nginx限流防刷与CC攻击防护实战配置

做过Web服务的都知道,接口裸奔是找死。

不设防的后果:羊毛党刷爆优惠券、爬虫拖垮服务器、CC攻击搞瘫业务。

整理一下Nginx层面的防护方案,都是生产环境验证过的配置。

一、限流基础

Nginx有两个核心限流模块:

  • ngx_http_limit_req_module:限制请求速率
  • ngx_http_limit_conn_module:限制连接数

1.1 限制请求速率

http {
    # 定义限流区域
    # $binary_remote_addr:按客户端IP限流
    # zone=req_limit:10m:共享内存区域名和大小
    # rate=10r/s:每秒10个请求
    limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
    
    server {
        location /api/ {
            # 应用限流
            # burst=20:允许突发20个请求
            # nodelay:突发请求不延迟处理
            limit_req zone=req_limit burst=20 nodelay;
            
            proxy_pass http://backend;
        }
    }
}

参数解释:

  • rate=10r/s:平均每秒10个请求,即每100ms一个
  • burst=20:桶大小,允许突发20个请求
  • nodelay:突发请求立即处理,不排队等待

1.2 限制连接数

http {
    # 按IP限制连接数
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
    
    server {
        location /download/ {
            # 每个IP最多10个连接
            limit_conn conn_limit 10;
            
            # 限制每个连接的速度
            limit_rate 1m;  # 1MB/s
        }
    }
}

适用场景:下载服务、视频流媒体。

二、分场景限流

2.1 登录接口防暴力破解

http {
    # 登录接口严格限流
    limit_req_zone $binary_remote_addr zone=login_limit:10m rate=1r/s;
    
    server {
        location /api/login {
            limit_req zone=login_limit burst=5 nodelay;
            
            # 超限返回429
            limit_req_status 429;
            
            proxy_pass http://backend;
        }
    }
}

每秒只允许1次登录请求,防止暴力破解。

2.2 短信验证码防刷

http {
    # 验证码接口更严格
    limit_req_zone $binary_remote_addr zone=sms_limit:10m rate=1r/m;
    
    server {
        location /api/sms/send {
            # 每分钟1次
            limit_req zone=sms_limit burst=3;
            
            proxy_pass http://backend;
        }
    }
}

2.3 搜索接口防爬虫

http {
    # 搜索接口中等限流
    limit_req_zone $binary_remote_addr zone=search_limit:10m rate=5r/s;
    
    server {
        location /api/search {
            limit_req zone=search_limit burst=10 nodelay;
            
            proxy_pass http://backend;
        }
    }
}

三、CC攻击防护

CC攻击就是用大量代理IP发请求,单IP限流不够用。

3.1 请求频率+并发连接双重限制

http {
    limit_req_zone $binary_remote_addr zone=cc_req:10m rate=30r/s;
    limit_conn_zone $binary_remote_addr zone=cc_conn:10m;
    
    server {
        # 请求频率限制
        limit_req zone=cc_req burst=50 nodelay;
        
        # 并发连接限制
        limit_conn cc_conn 50;
        
        # 超限状态码
        limit_req_status 503;
        limit_conn_status 503;
    }
}

3.2 基于User-Agent过滤

server {
    # 封禁空UA和常见爬虫UA
    if ($http_user_agent = "") {
        return 403;
    }
    
    if ($http_user_agent ~* "python|curl|wget|scrapy|httpclient") {
        return 403;
    }
    
    # 封禁特定UA
    if ($http_user_agent ~* "MJ12bot|AhrefsBot|SemrushBot") {
        return 403;
    }
}

3.3 基于Referer过滤

server {
    location /api/ {
        valid_referers none blocked server_names *.example.com;
        
        if ($invalid_referer) {
            return 403;
        }
    }
}

3.4 cookie验证

server {
    location / {
        # 检查是否有验证cookie
        if ($cookie_verified != "yes") {
            # 返回验证页面,通过JS设置cookie后重定向
            return 302 /verify.html;
        }
        
        proxy_pass http://backend;
    }
}

这个方法可以过滤掉不执行JS的简单爬虫。

四、IP黑白名单

4.1 黑名单

http {
    # 加载黑名单文件
    geo $blocked_ip {
        default 0;
        include /etc/nginx/blacklist.conf;
    }
    
    server {
        if ($blocked_ip) {
            return 403;
        }
    }
}

黑名单文件:

# /etc/nginx/blacklist.conf
1.2.3.4 1;
5.6.7.0/24 1;

4.2 白名单

http {
    geo $whitelist {
        default 0;
        10.0.0.0/8 1;      # 内网
        192.168.0.0/16 1;  # 内网
    }
    
    server {
        location /admin/ {
            if ($whitelist = 0) {
                return 403;
            }
            
            proxy_pass http://backend;
        }
    }
}

4.3 动态封禁

配合fail2ban实现自动封禁:

# /etc/fail2ban/filter.d/nginx-cc.conf
[Definition]
failregex = ^<HOST> .* "(GET|POST).* HTTP.*" (429|503)
ignoreregex =
# /etc/fail2ban/jail.d/nginx-cc.conf
[nginx-cc]
enabled = true
port = http,https
filter = nginx-cc
logpath = /var/log/nginx/access.log
maxretry = 100
findtime = 60
bantime = 3600

60秒内触发100次429/503就封禁1小时。

五、实战配置模板

综合以上方案的完整配置:

http {
    # 限流区域定义
    limit_req_zone $binary_remote_addr zone=global_limit:20m rate=50r/s;
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=20r/s;
    limit_req_zone $binary_remote_addr zone=login_limit:10m rate=1r/s;
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
    
    # 黑名单
    geo $blocked_ip {
        default 0;
        include /etc/nginx/blacklist.conf;
    }
    
    # 白名单
    geo $whitelist {
        default 0;
        10.0.0.0/8 1;
        192.168.0.0/16 1;
    }
    
    server {
        listen 80;
        server_name example.com;
        
        # 黑名单拦截
        if ($blocked_ip) {
            return 403;
        }
        
        # 空UA拦截
        if ($http_user_agent = "") {
            return 403;
        }
        
        # 全局限流
        limit_req zone=global_limit burst=100 nodelay;
        limit_conn conn_limit 100;
        
        # API接口限流
        location /api/ {
            limit_req zone=api_limit burst=30 nodelay;
            proxy_pass http://backend;
        }
        
        # 登录接口严格限流
        location /api/login {
            limit_req zone=login_limit burst=5 nodelay;
            limit_req_status 429;
            proxy_pass http://backend;
        }
        
        # 静态资源不限流
        location /static/ {
            expires 7d;
            add_header Cache-Control "public, immutable";
        }
        
        # 管理后台白名单
        location /admin/ {
            if ($whitelist = 0) {
                return 403;
            }
            proxy_pass http://backend;
        }
    }
}

六、监控与告警

光限流不够,还要能看到发生了什么。

6.1 日志格式

log_format detailed '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent" '
                    '$request_time $upstream_response_time '
                    '$limit_req_status';

access_log /var/log/nginx/access.log detailed;

6.2 实时统计

# 统计429状态码
tail -f /var/log/nginx/access.log | grep " 429 " | wc -l

# 按IP统计请求数
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

6.3 自定义限流日志

location /api/ {
    limit_req zone=api_limit burst=30 nodelay;
    
    # 限流日志
    limit_req_log_level warn;
}

被限流的请求会记录到error.log:

limiting requests, excess: 30.234 by zone "api_limit"

七、压测验证

配置完要验证效果:

# 用ab压测
ab -n 1000 -c 100 http://example.com/api/test

# 用wrk压测
wrk -t4 -c200 -d30s http://example.com/api/test

观察:

  • 正常请求能过
  • 超限请求返回429或503
  • 服务器资源没被打满

八、远程管理

我们有几个边缘节点部署了Nginx做CDN,分布在不同城市。之前更新配置很麻烦,现在用星空组网把所有节点组到一起,SSH直连更新配置很方便。

总结

Nginx防护策略:

攻击类型 防护方案 核心配置
单IP刷接口 请求速率限制 limit_req
下载带宽滥用 连接数+速度限制 limit_conn + limit_rate
暴力破解 严格限流 rate=1r/s
CC攻击 多层防护 限流+UA过滤+黑名单
爬虫 UA+Referer过滤 if判断

防护策略要分层:

  1. 第一层:IP黑名单
  2. 第二层:速率限制
  3. 第三层:行为判断
  4. 第四层:验证码(业务层)

别等被攻击了才想起来加防护。


有其他防护方案欢迎分享~


posted @ 2025-12-23 10:01  花宝宝  阅读(42)  评论(0)    收藏  举报