必须同时做图形验证、后端频次控制和风险识别,缺一不可;thinkphp6 captcha默认依赖session,api场景需显式传id并启用sessioninit中间件;短信限频须用redis按手机号+类型原子计数;ocr防护不足时应接入tcaptcha等第三方动态验证。

短信验证码接口被暴力调用,不是“加个验证码就完事”,而是必须同时做图形验证 + 后端频次控制 + 风险识别。只做其中一环,基本等于没防。
ThinkPHP 6.0 中 captcha 组件默认不校验 session?
是的,默认启用 Captcha::create() 后,前端拿到图片,但后端调用 Captcha::check($verify) 时,若未显式传入 $id 或未开启 session(比如在无状态 API 场景下),check 会始终返回 false,或误判为已过期——因为 ThinkPHP 6 的 captcha 默认把验证码值存在 session 里,且 key 是动态生成的(除非你手动指定 $id)。
实操建议:
- 确保控制器中已调用
session_start()或通过中间件启用 session 支持(如think\middleware\SessionInit) - 生成验证码时显式传入固定
$id:Captcha::create('sms_login') - 校验时必须用相同
$id:Captcha::check($input, 'sms_login') - 不要依赖前端传来的随机 token 做校验 key,那容易被绕过
短信发送接口如何限制同一手机号 24 小时内最多 5 次?
不能只靠前端倒计时或 JS 禁用按钮,攻击者直接发 HTTP 请求就能绕过。必须在 ThinkPHP 控制器里做服务端强校验,且需结合 Redis 记录频次(数据库太慢,扛不住并发刷)。
关键点:
- 用手机号 + 操作类型(如
sms_register)拼成 Redis key:rate_limit:138****1234:sms_register - 使用
incr+expire原子操作:先$redis->incr($key),再$redis->expire($key, 86400)(避免首次写入没过期时间) - 判断返回值是否 > 5,大于则直接
return json(['code'=>403, 'msg'=>'今日发送次数已达上限']) - 别用
setex单独设值再 incr——竞态条件下可能漏统计
图形验证码防 OCR 效果差?ThinkPHP 6 怎么换更安全的方案?
ThinkPHP 自带的 captcha 是基础文字扭曲图,对现代 OCR(尤其是 PaddleOCR、EasyOCR)识别率高达 80%+,不适合高风险场景。它不支持滑动拼图、点选文字等动态验证方式。
可落地的替代路径:
- 停用内置
captcha,接入第三方轻量 SDK,例如腾讯云「验证码」(tcaptcha)或极验(geetest),它们提供 PHP SDK 和服务端校验接口 - 若必须自研,不要自己画图+OCR对抗,而应转向行为特征采集:记录鼠标轨迹、点击时序、设备指纹(
$_SERVER['HTTP_USER_AGENT']+$_SERVER['HTTP_ACCEPT_LANGUAGE']+ Canvas Fingerprint 等),再结合 Redis 做风险评分 - 对低风险请求(如老用户、同 IP 多次成功)降级显示简单验证码;对新设备、高频请求自动升级为滑块验证
真正难防的不是单点破解,而是攻击者用代理 IP + 手机号池 + 打码平台三连击。所以频次限制得按「手机号 + IP + 设备指纹」三个维度分别计数,再加一个总熔断阈值——这点多数人会漏掉。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











