验证码
从「能不能复用」到「能不能识别」,以及为什么识别类绕过在报告里通常不算高危。
先试最简单的三件事
拿到一个带验证码的登录口,别急着上工具。先试这三条:
- 验证码能不能复用。登录失败后,同一个验证码再提交一次,看服务端认不认。
- 验证码有没有过期。拿到图片,等十分钟再提交。
- 验证码有没有跟会话绑定。换一个 session,用原来的验证码。
这三条只要有任意一条成立,爆破就没有障碍,不需要任何识别技术。而这三条在实际系统里成立的比例,比想象中高。
很多系统在前端做了「刷新验证码」,但服务端只校验「这个码在不在已发放的池子里」,不校验「是不是这个会话的」——池子共享,等于没有。
识别类绕过
三条都不成立,才轮到识别。
思路很直接:程序自动取验证码图片 → OCR 识别 → 填进登录请求。识别率够高,爆破就能跑起来。
工具链是现成的:
- ddddocr:离线 OCR 库,专为验证码场景训练。
ocr.classification(img_bytes)一行出结果,支持限定字符集(纯数字、纯字母、算术式)。 - captcha-killer-modified:Burp 插件,把「取验证码 → 调识别接口 → 回填」串进 Intruder。
- Yakit:用
beforeRequest/afterRequest两个钩子做同样的事,不用装插件。
识别服务通常是一个几十行的 HTTP 服务:收 base64 图片,返回识别结果。放在本地跑,不依赖外部接口。
算术验证码(3+5=?)也能识别,识别出算式之后自己算一下再提交,命中率比纯字符高。
识别率决定可行性
一个四位字符的验证码,识别率 90%,意味着平均 1.1 次能过一次。配合密码字典,几千次尝试就能跑完。
识别率 50%,就需要两次一次成功,请求量翻倍,很容易触发频率限制。
所以测这类问题时,先跑 100 张看识别率。低于 60% 就别往下做了,报告里写「验证码可被自动识别,识别率 xx%」,比硬跑出结果更诚实。
为什么这类问题通常不定高危
「验证码可被 OCR 识别」单独拿出来,只是一个「防护措施强度不足」。它要变成高危,必须叠加一个前提:没有其他登录保护。
如果系统同时有:账号锁定(5 次失败锁定 30 分钟)、异地登录告警、强密码策略——那验证码被识别只是让爆破变慢,不构成实际风险。
如果系统什么都没有,只有验证码这一道,那它就是唯一的防线,被绕过等于没有。
报告里写这个区别,客户才知道该先修哪个。
顺带一提:短信验证码
登录、注册、找回密码三个入口的短信验证码,问题往往比图形验证码多:
- 验证码 4 位、6 位数字,有效期 5 分钟,可爆破。
- 校验通过后不失效,同一码可重复用。
- 手机号和验证码不绑定,A 的码能验证 B 的手机号。
- 返回包里有
code字段,前端校验后端不校验。
第四条最常见:把响应里的 {"success":false} 改成 true,前端就跳转了,后端根本没验。
修复
- 验证码与会话绑定,一次性使用,60 秒过期。
- 图形验证码不要太简单,也别太复杂——太复杂用户体验差,且挡不住 OCR。
- 登录失败次数限制,按账号 + IP 双维度。
- 短信验证码:6 位、5 分钟、一次有效、与手机号绑定、后端校验。
- 关键操作(改密码、转账)加二次验证。
参考
- 《验证码绕不过去》,公众号 瑶光安全团队Alkaid