CC 攻击的特点是”像真人一样访问”:来源是分散的真实 IP,请求的又都是数据库密集的动态页面,所以单纯限频和封 IP 往往治不住。人机验证(验证码)是少数能直接抬高攻击成本的手段。本文用 8 个问答讲清验证码防 CC 的原理、配置方法和副作用控制。
Q:验证码真能挡住 CC 攻击吗?
能挡一部分,但要摆正预期。验证码解决的是”区分人和脚本”,不解决”流量有多大”。它的作用是把攻击成本从”发个 HTTP 请求”抬到”过一次人机验证”,让攻击方必须调用打码平台或大量真人众包,成本直接翻几十倍。所以对纯脚本型、低成本 CC 工具,验证码效果非常明显;对已经接入打码平台或真人众包的定向攻击,验证码只能拖慢,不能根除。结论:验证码是 CC 防护链条里必要的一环,但不能当唯一手段。
Q:为什么验证码对 CC 特别有效?
因为 CC 攻击绕不开一个前提:请求必须是”有效请求”才有杀伤力。如果服务端要求每个请求先完成一次人机校验,那么攻击方每发一次有效请求,都得先付一次验证成本。成本结构变了:
- 纯脚本发 1 万次请求耗时以秒计,要过验证码则必须调用打码接口,按次付费;
- 打码平台有识别成功率上限,失败重试会进一步放大成本;
- 一旦成本高于攻击收益(比如只是为了打掉一个普通网站),攻击方通常会主动放弃换目标。
反过来说,如果验证码能在边缘节点(CDN/WAF)完成,源站连这次校验请求都收不到,防护效果和服务器压力都最优。
Q:常见的验证方式有哪几种?强度怎么排?
从弱到强大致四档:
- 图形数字字母:最弱,OCR 和打码平台基本秒破,只适合防最原始的脚本;
- 滑块/拖拽:中等,靠轨迹和行为特征判断,能挡住大部分简单自动化,但对专门做轨迹模拟的工具有一定损耗;
- 点选文字/语义题:中等偏强,需要理解语义,纯 OCR 难以直接通过,打码成本较高;
- 无感验证(JS 质询 + 行为指纹):最强且体验最好,正常用户几乎无感,靠浏览器环境、事件时序、指纹特征综合打分,纯脚本很难完整模拟。
实战建议:优先上无感验证,把点选/滑块作为降级方案,只在评分异常时才弹出。这样既挡住攻击,又不会让所有访客都做题。
Q:怎么做到”只在被攻击时才弹验证码”?
关键是按频率动态触发,而不是全站常开。先用 Nginx 限频标记可疑来源,再对可疑来源引导到验证页。下面是可用的思路性配置(limit_req 只做判断,返回 429 后由前端/网关接管):
http {
# 按 IP 记请求频率,超出即判为可疑
limit_req_zone $binary_remote_addr zone=cczone:10m rate=10r/s;
limit_req_status 429;
server {
# 静态资源不参与判断,避免误伤
location ~* \.(css|js|png|jpg|jpeg|gif|ico|woff2?)$ {
expires 7d;
access_log off;
}
# 动态页面:先限频,超限的交给验证层
location = /search {
limit_req zone=cczone burst=20 nodelay;
proxy_pass http://backend;
}
# 命中 429 时返回一个带验证码的页面或 302 到验证入口
error_page 429 = /verify.html;
location = /verify.html {
root /var/www/static;
internal;
}
}
}
参数要点:rate=10r/s 是每 IP 每秒 10 个请求,正常用户点不出这个速度;burst=20 允许短时突发;nodelay 避免正常用户被排队拖慢。静态资源必须排除,否则页面里的图片会把正常访客的计数刷爆,造成大面积误伤。
Q:验证码会不会误伤正常用户?怎么控制触发面?
会,而且这是验证码方案最大的坑。降低误伤的四个做法:
- 只对高消耗路径开启:登录、搜索、列表翻页、提交表单等吃数据库的接口开,纯静态页面不开;
- 先无感后显性:先用无感验证打分,分数低才弹可见验证码,正常用户全程无感;
- 用白名单排除自家和搜索引擎:把公司出口 IP、监控探针 IP、主流搜索引擎蜘蛛加入白名单;
- 做灰度:先对 10% 流量启用,观察误伤率和报错量,再逐步放开。
另外必须准备降级预案:验证服务本身被打挂或被误判时,要能一键关闭验证直接放行,避免”防攻击反而把正常业务全挡了”。
Q:对方用打码平台或 AI 破解怎么办?
说明攻击是定向的,靠单一验证方式已经不够。这时要做的是叠加维度、提高单次成本:
- 缩短会话有效期:验证通过后发放的通行证 Cookie 设 5~10 分钟过期,迫使攻击方反复验证;
- 绑定会话指纹:通行证与 IP、UA、TLS 指纹绑定,换一个组合就作废,代理 IP 池的优势被削弱;
- 加业务验证:关键操作(下单、查详情)前加一次性 Token,Token 由服务端下发且只能用一次,纯刷 URL 的方式直接失效;
- 行为侧联动:同一会话内访问路径、停留时间、鼠标/滚动事件异常的一律降权,交给更严的验证档位。
核心逻辑是:不要试图让验证码不可破,而要让破一次的成本高于收益。打码平台是按次收费的,你能把每次有效请求的成本抬到几分钱,大多数攻击就不划算。
Q:验证码之外,还必须配哪些措施?
验证码只负责”过滤”,流量压力还得靠其他层分担,缺一层都是短板:
- 静态化与缓存:首页、列表页尽量走缓存或生成静态,攻击打的是缓存而不是数据库,这是性价比最高的减伤手段;
- 频率与并发限制:Nginx 的
limit_req/limit_conn做第一道兜底:
limit_conn_zone $binary_remote_addr zone=ccconn:10m;
server {
location / {
limit_conn ccconn 15; # 单 IP 并发连接上限
}
}
- IP/UA 黑名单:日志里频率异常或 UA 明显为工具的来源直接封禁,作为应急止血;
- 源站隐藏:源站 IP 一旦暴露,攻击者可以绕过 CDN 直打源站,所有边缘防护全部失效;
- 边缘防护:把限频、验证、缓存放到 CDN/WAF 节点,源站只收干净流量,服务器才有余力处理正常业务。
Q:还有什么补充建议?
给一套落地顺序:第一步先减负——把动态页面改成缓存或静态,攻击杀伤力先掉一半;第二步再限频——用 limit_req/limit_conn 挡住低强度持续刷;第三步上无感验证——只对高消耗路径开,配合频率触发,正常用户无感;第四步加固会话——通行证短时效 + 指纹绑定 + 一次性 Token。最后提醒两点:验证码不是越严越好,过严会把真实用户挡在门外,任何调整都要看误伤率;被攻击期间要把日志留存好,攻击来源、UA、路径都是后续加白加黑的依据,也是必要时报案的支撑材料。