滑动拼图验证码后端校验必须绑定challenge_id、HMAC签名、Redis时效性与±5像素容错,缺一不可;需动态生成带视觉扰动的背景图与滑块图,校验时须同时验证challenge_id存在未过期、时间戳防重放、签名正确性及offset容差,不可复用base64Captcha库。

滑动拼图验证码不是前端拖完就完事,后端校验必须绑定 challenge_id、HMAC 签名、Redis 时效性与 ±5 像素容错——缺一不可,否则等于没设防。
生成带随机缺口的背景图与滑块图
不能复用静态图,也不能直接裁剪原图。攻击者可预计算所有 offset 的像素哈希值,直接匹配绕过。必须每次动态生成并加入视觉扰动:
- 用
image.NewRGBA创建画布,先填充噪点或简单纹理(如多色渐变+轻微高斯模糊) - 缺口横坐标用
crypto/rand生成(别用math/rand+time.Now().UnixNano(),并发下易重复),范围避开边缘(如 80–240px) - 滑块尺寸固定为 40×40,从背景图
subImage裁出后,立即对滑块图做 ±3° 旋转 + 2% 亮度抖动 - 背景图上用同色块“盖住”缺口位置,制造缺失感;两张图都以
png.Encode输出,响应头设Content-Type: image/png
校验接口必须验证四个维度
只比对 slider_offset 数值?等于给自动化工具递钥匙。合法请求需同时满足:
-
challenge_id存在且未过期(Redis key 为captcha:{uuid},TTL 设 120 秒) - Redis value 中的
ts与当前时间差 ≤ 30 秒(防重放) - 客户端提交的
signature必须等于hmac-sha256(challenge_id + slider_offset + salt, secret_key)(salt 随 challenge 生成,不硬编码) -
slider_offset与 Redis 中存的real_offset差值 ≤ 5(允许前端因缩放/四舍五入产生的误差,但该容差值必须由后端下发,不能写死在 JS 里)
Gin 路由中避免中间件陷阱
有人把验证码生成塞进 Gin 中间件,结果要么每次请求都生成新图,要么校验时 context 已销毁。正确做法是拆成两个独立 handler:
Colly 是一个用于 Go 语言的快速开源爬取和爬虫框架。它适用于从简单的页面提取到异步爬虫处理大量页面集合,支持请求回调和结构化解析。
立即学习“go语言免费学习笔记(深入)”;
- GET
/captcha:不走鉴权中间件,返回{"id": "xxx", "bg": "data:image/png;base64,...", "piece": "data:image/png;base64,...", "salt": "abc"} - POST
/login或业务接口:在 handler 开头手动调用校验逻辑,例如:if !verifySlider(c.Param("id"), c.PostForm("offset"), c.PostForm("signature")) { ... } - 切忌在中间件里调用
c.Next()后再取参数——此时 request body 可能已被读空,c.PostForm返回空字符串
为什么不能用 base64Captcha 库做滑块校验
base64Captcha 是为字符型验证码设计的,它的 VerifyString 方法只比对文本答案,不支持 offset 容错、签名验签、挑战绑定等滑块必需逻辑。强行套用会导致:
- 无法下发
salt和容差值,前端不知道该拖到哪、允许多大误差 - 存储层默认用内存 map,服务重启后所有 challenge 失效,且无法跨实例共享
- 没有 HMAC 签名机制,
offset参数可被任意篡改,抓包重放一次即全量通过
滑块的核心不在绘图,而在 challenge 生命周期管理与原子性校验——这部分必须手写,绕不开 Redis 与 crypto/hmac。

















