YzmCMS自研汉字点选验证码

82次浏览 更新日期:2026-09-16 23:06:19 分类:模板插件 评论:0

不知大家网站评论区是否经常收到一些垃圾信息,明明已经添加了验证码,还是莫名其妙的被各种机器刷爆了评论区。于是经过半个月的苦心钻研,我开发了一款汉字点选验证码插件,它用 PHP GD 库生成、纯 jQuery前端交互,不依赖任何第三方服务。我会从攻防视角出发,对比它与传统字符验证码、滑块验证码的优劣,并拆解它背后的安全设计。

YzmCMS自研汉字点选验证码

一、三种验证码的攻防现状

1. 字符验证码:几乎已被 OCR 淘汰

最经典的验证码——把数字和字母画成扭曲、加干扰线、加噪点的图片,让用户输入。

它的问题是根本上的:字符验证码的"答案"就写在图片里,机器只要"认字"即可。深度学习模型(如 ddddocr)对常见字符验证码的识别率已经超过 99%,而且开箱即用,攻击者一行代码就能接入。开发者越扭曲字符,人越难认,但对神经网络几乎不构成障碍。

这是一场注定失败的军备竞赛。

2. 滑块验证码:曾经好用,现在也被自动化攻克

滑块验证码要求用户拖动拼图块到缺口处。它的思路是"不考知识,考行为"——通过拖动轨迹判断是否人类。

但它有两个致命弱点:

缺口位置可被图像算法精确求出。主流做法是对背景图做边缘检测/模板匹配,或直接用 ddddocr 的目标检测模型定位缺口,误差通常在 1~2 像素内。

拖动轨迹可以被模拟。把"人类滑动曲线"建模成带加速度的贝塞尔曲线,或直接录制真人轨迹回放,服务端的轨迹校验很容易被绕过。

我实测过用 ddddocr + Puppeteer 自动过滑块,从识别缺口到拖完提交,整个流程不到 1 秒,成功率接近 100%。一旦攻击者有了这套脚本,滑块验证码就形同虚设。

更糟糕的是,滑块验证码答案唯一——缺口坐标是固定的,机器只要算出一个数就够了。

3. 点选验证码:把"认字"变成"定位 + 排序"

点选验证码的做法是:在一张图上随机放几个汉字,告诉用户"请按顺序点击:春、江、花、月",用户需要:

1.识别出每个目标字在图上的位置(认字 + 定位)

2.按顺序依次点击

3.点击坐标在服务端预设的容差范围内才算通过

它的核心优势是:机器既要"认字",又要"找到字在哪",还要"记住顺序"。这比字符验证码多了空间定位和序列记忆两个维度,比滑块多了内容理解这个维度。

二、这款点选验证码插件的安全设计

1. 防 OCR / 模板匹配

生成汉字时,我没有用纯色填充,而是做了双色纵向渐变 + 颗粒纹理抖动:

每个字用两种随机亮色做纵向渐变填充

每个像素再叠加 ±7 的随机亮度抖动(颗粒纹理)

字号、倾斜角度(±22°)随机

渐变和颗粒让像素直方图分布变平,模板匹配的相关度峰值被削弱;旋转破坏了字形的规整性,OCR 需要先做文字方向校正才能识别。

同时画了 2 条亮色直线随机穿过某个字的中心,切断字形笔画的连续性,干扰目标检测模型对字符的分割。

2. 防背景扣除攻击

滑块的缺口能用"模板图 - 背景图"求差来定位。点选验证码如果直接用固定背景图,攻击者拿到原图后也能做背景扣除,把字抠出来。

对策:每次生成都对背景图做 cover 裁剪 + 随机放大取景(65%~100%)+ 随机水平翻转。同一张背景图,每次出现的是不同的子区域、不同的缩放、可能还镜像翻转,原图和验证码图无法像素对齐,背景减法失效。

这也是为什么背景图目录建议放在 webroot 之外——不让攻击者直接拿到原图。

3. 答案坐标不泄露

前端收到的只有:

验证码图片 base64

要点击的文字顺序(如 ["春","江","花","月"])

答案坐标只存在服务端 session 里,前端提交点击坐标后由服务端做圆形容差校验。攻击者无法从接口返回中拿到正确答案。

4. 一次性通行证(pass_token)

验证通过后服务端签发一个一次性 pass_token,业务接口(如登录、评论)凭此 token 放行,consumePassToken() 验过即删。

token 用 random_bytes(或 openssl CSPRNG 兜底)生成,不可预测

一次性使用,无法重放

绑定 session,跨会话无效

这防止了"脚本拿到一次验证结果反复提交业务请求"的攻击。

5. 速率限制 + 失败锁定

生成接口:15 次/分钟

校验接口:10 次/分钟

同一 IP 连续失败 5 次后锁定 5 分钟(含倒计时)

滑动窗口限流存在临时文件里,失败计数也按 IP 维度持久化。限流是验证码的最后一道防线——即使识别算法很强,也让爆破成本指数级上升。

6. 行为校验

服务端不只看坐标对不对,还看用户怎么点的:

耗时:从图片生成到提交,必须在 0.8~60 秒之间。太快(< 0.8s)判定为机器人,太慢说明过期。

鼠标轨迹:前端采集从打开弹窗到点完 4 个字的鼠标移动轨迹,服务端校验:

轨迹点数 ≥ 20

平均速度 > 0.01

速度方差不能太小(不能匀速)

路径总长度 / 直线距离 > 1.3(不能走直线)

移动时间间隔方差不能太小(不能等间隔)

这些条件组合起来,让"算好坐标直接伪造点击"的脚本失效——它还需要模拟一条像人的鼠标轨迹。

7. CSRF 防护

每次生成验证码随图返回一个 CSRF token,提交时必须原样带回,与 session 中存储的比对。防止跨站请求伪造。

三、性能与兼容性

图片生成耗时约 32ms(PHP 8 环境),图片用 JPEG 质量 85 编码,单张约 22KB,加载快,兼容 PHP 5.6 32位 到 PHP 8.x(imageflip 有手动列镜像兜底,imageline 坐标做了 (int) 强转)

前端依赖 jQuery 1.7+,无其他依赖

当然任何验证码都有被攻破的可能,点选验证码也不例外:

图片生成后,字的位置对机器是可学习的。如果攻击者专门针对你的字库和渲染风格训练目标检测模型,识别率会逐步上升。对策是定期更新字库和背景图库、调整渲染参数(角度范围、干扰线数量等)。

行为校验有一定误伤率。网络慢、操作慢的用户可能触发"耗时过长"或"轨迹点不足"。可以通过调高 maxCostSec 和放宽轨迹阈值来平衡。

它不能替代风控。验证码是第一道闸门,登录风控、IP 信誉、设备指纹等手段需要配合使用。

该验证码演示地址:

https://blog.yzmcms.com/php/311.html  或  YzmCMS演示站 均已上线,后续升级无影响。


我来说两句
作者信息
发布见解
发内容 回顶部