验证码能挡住CC攻击吗?人机验证实战配置

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、路径都是后续加白加黑的依据,也是必要时报案的支撑材料。