ThinkPHP5验证码需通过干扰增强、时效频次控制、前后端协同及辅助防线四方面加固;默认配置无防护力,须启用噪点曲线、设置120–300秒过期、绑定会话限频、校验前trim和小写、确保Session中间件启用,并叠加CSRF Token、IP限流与隐藏字段等措施。

ThinkPHP5 的验证码本身不防破解,它只提供基础图像生成和比对能力;真正起防护作用的是你如何配置、调用和配合其他机制。防不住自动化识别或暴力尝试,不是因为类写得差,而是默认配置太“干净”——没加干扰、没设时限、没绑会话、没控频次。
干扰增强:让OCR识别率大幅下降
默认的验证码图太规整,容易被开源 OCR 工具(如 Tesseract)批量识别。启用噪点和曲线是成本最低、见效最快的加固方式:
- useNoise => true:添加随机像素噪点,打乱字符边缘连续性
- useCurve => true:绘制多条非规则贝塞尔曲线,覆盖部分字符但不遮挡可读性
- 搭配 fontSize ≥ 20 和 length = 4–5,避免过小或过长导致识别率反弹
- 禁用中文(useZh => false)和背景图(useImgBg => false),减少纹理干扰带来的误识别风险
时效与频次控制:卡住暴力尝试窗口
验证码一旦生成就长期有效,攻击者可离线穷举;若不限制校验次数,1秒内试几百次很常见。必须双管齐下:
-
expire 设为 120–300 秒:在
Captcha实例化时传入,例如new Captcha(['expire' => 180]) -
Session 绑定失败计数:每次
check()失败后,记录当前 IP 或 session_id 的错误次数,达 5 次即临时封禁该会话 10 分钟(可用session('verify_fail_count', $n)+ 时间戳判断) - 校验通过后务必调用
$captcha->reset(),防止同一验证码被重复提交
前后端协同防绕过:避免“形同虚设”
很多项目验证码显示正常、校验逻辑也写了,却一抓一个准——问题出在链路断点上:
立即学习“PHP免费学习笔记(深入)”;
-
前端必须用 {:captcha_img()} 或 {:captcha_src()},别手写
/captcha路径,否则在子域名、PATHINFO 模式或自定义路由下极易 404 或跨域失效 -
用户输入要 trim() + strtolower():空格、大小写不一致是
check()返回 false 的最常见原因 -
确保 Session 中间件已启用且生效:TP5.1+ 必须在
app/middleware.php中开启\think\middleware\SessionInit::class,并验证session('test', 'x'); var_dump(session('test'))输出是否为'x' - 表单提交 URL 若带 query 参数(如
?id=123),而验证码按路径生成,会导致check()内部比对路径不一致——此时需统一用无参路径生成,或改用captcha_check($code, '', false)忽略参数校验
进阶补充:不依赖验证码的辅助防线
验证码只是第一道门,不能当作唯一屏障。建议叠加以下低成本措施:
-
CSRF Token 强制校验:开启
'token_on' => true,并在登录/注册控制器开头调用$this->request->checkToken(),阻断脚本伪造请求 - IP 请求频率限制:用中间件统计同一 IP 在 60 秒内对验证码接口的调用次数,超 10 次即返回 429
- 隐藏式人机判断字段:表单中加一个 type="hidden" 字段,值为服务端生成的 base64 时间戳签名,提交时校验是否过期且未篡改
- 敏感操作(如密码重置)建议增加短信/邮箱二次确认,验证码仅作前置过滤



















