实现滑动验证码,后端只需做两件事:造一张带缺口的拼图并记住正确位置,再对前端上报的滑动轨迹做”位置匹配 + 行为自然性”双重校验。单纯比拼图位置会被脚本绕过,真正拉开人机差距的是轨迹——机器走直线、匀速、零抖动,真人则有起停、变速和轻微回拉。下面给出可落地的实现与防破解策略。
一、滑动验证码判定的两个维度
滑块验证码不考”你认不认识字符”,而考”你的操作像不像人”。判定集中在两个层面:图像层面看拼图是否落在缺口,行为层面看拖动轨迹是否自然。
| 判定维度 | 机器特征 | 人类特征 |
|---|---|---|
| 轨迹形态 | 直线、匀速、教科书曲线 | 曲线变速,带停顿与抖动 |
| 移动速度 | 恒定或规律性变化 | 忽快忽慢,不规则 |
| 起止位置 | 精准定位缺口 | 有偏差,会二次修正 |
| 滑动耗时 | 极短或极规律 | 符合肌肉控制的生理节奏 |
| 加速度 | 无起伏 | 由慢到快再到慢的过渡 |
图像校验只能挡住最笨的脚本,行为校验才是防线核心。
二、后端造图:拼图与缺口的生成
后端随机选底图,随机算缺口坐标 (x, y),用图像算法抠出拼图块,底图与拼图块转 Base64 下发,正确 X 存入 Redis 并设短 TTL。
2.1 Python 造图示意
import random
from PIL import Image, ImageDraw
def build_puzzle(bg_path: str, block_w=42, block_h=42, offset=5):
bg = Image.open(bg_path).convert("RGB")
w, h = bg.size
x = random.randint(block_w + 10, w - block_w - 10)
y = random.randint(10, h - block_h - 10)
block = bg.crop((x, y, x + block_w, y + block_h))
draw = ImageDraw.Draw(bg)
draw.rectangle((x, y, x + block_w, y + block_h), fill=(255, 255, 255))
return bg, block, x, y, offset
# 正确 X 落入 Redis:SET captcha:order123 {x} EX 120
2.2 正确位置存 Redis
正确坐标只在服务端缓存,键与本次会话绑定(如订单号或 token),TTL 取 2 分钟;前端只拿底图、拼图块与缺口纵向提示,横向正确值由服务端闭环校验。
三、前端轨迹采集
用户拖动滑块时,前端按帧采集坐标与时间戳,松手时连同最终偏移量一起上报。采样点含横坐标、纵坐标、相对起始时间(毫秒)与该帧速度。
let track = [], start = null;
slider.onmousedown = => start = Date.now;
slider.onmousemove = e => {
if (start) track.push({ x: e.clientX, y: e.clientY, t: Date.now - start });
};
四、后端双重校验
第一步比位置,第二步比轨迹。两步都过才放行。
- 取出 Redis 中的正确 X,判断上报偏移量与它的误差是否落在允许范围(默认 5 像素);
- 若位置误差过大,直接判失败,不进入轨迹分析,省算力;
- 用轨迹计算平均速度、加速度方差、是否有过冲回拉、总耗时;
- 规则或模型判定轨迹”过于完美”则驳回,并升级风控(二次验证或封禁会话)。
4.1 轨迹自然性校验逻辑
def check_track(track, target_x, offset, offset_tol=5):
if abs(offset - target_x) > offset_tol:
return False, "position mismatch"
if len(track) < 5:
return False, "too few points"
duration = track[-1]["t"] - track[0]["t"]
if duration < 300:
return False, "too fast"
speeds = []
for i in range(1, len(track)):
dt = max(track[i]["t"] - track[i-1]["t"], 1)
dx = abs(track[i]["x"] - track[i-1]["x"])
speeds.append(dx / dt)
mean_s = sum(speeds) / len(speeds)
var = sum((s - mean_s) ** 2 for s in speeds) / len(speeds)
if var < 1e-4:
return False, "too uniform"
if max(speeds) == min(speeds):
return False, "no jitter"
return True, "ok"
生产环境会把这类阈值交给机器学习模型,用历史样本训练区分度更高的边界。
五、防机器破解的多维加固
只靠一条轨迹规则远远不够,破解者会用贝塞尔曲线拟合出带抖动的人类轨迹,通过率可以拉到 90% 以上。需要把多种信号叠加:浏览器指纹识别模拟器与多账号,行为模型评分替换硬编码阈值,滑块边缘混淆抬高机器识别成本,请求环境一致性校验防代理池轮换,频控与风控联动拦批量撞库。
常见问题(FAQ)
Q1:只校验拼图位置够不够?
不够。脚本能直接算出缺口坐标,必须叠加轨迹行为校验。
Q2:正确坐标能放前端吗?
不能。正确 X 必须只在服务端存 Redis,前端只收底图与拼图块。
Q3:轨迹规则被拟合绕过怎么办?
上机器学习评分,结合浏览器指纹与请求环境一致性多维判定。