尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

短信验证码逻辑缺陷剖析:从爆破原理到edusrc实战与修复

短信验证码逻辑缺陷剖析:从爆破原理到edusrc实战与修复 前阵子做教育业务系统授权测试碰到一个找回密码的短信验证码接口四条典型的逻辑缺陷凑在一起发送不限频、校验不限次、验证码不绑手机号、验证码有效期还是十分钟。我和搭档开玩笑说这四个口子凑齐别说验证码是四位哪怕六位也只是一只脚本多跑一会的事。后来翻edusrc平台教育行业漏洞报告平台上的漏洞趋势验证码逻辑缺陷仍然是提交量最大、也最容易出彩的方向之一。这篇想聊聊四位数验证码背后的逻辑缺陷从机制原理、常见类型、测试流程到修复建议把你在授权测试里需要确认的东西说清楚。适合正在做教育行业安全测试的朋友也适合刚接触SRC漏洞挖掘、想从验证码爆破和短信轰炸这类入门方向积累经验的读者。1. 验证码逻辑缺陷的根源四位数只是表象1.1 一条验证码的完整生命周期里藏着哪些环节短信验证码看起来是个很简单的功能从前端点一下“获取验证码”到最终提交表单校验通过中间其实有五个环节生成、下发、存储、校验、失效。任何一环写得糊弄都可能在安全测试里变成突破口。生成环节要关注随机数的强度和位数。四位纯数字理论上只有10000种组合空间本来就小如果随机数生成还依赖弱随机算法甚至用时间戳直接当种子那验证码就存在可预测的风险。下放环节要看发送通道有没有频率控制同号限制、IP限制、时间窗口有没有做这决定了一个号能不能在短时间内被反复“轰炸”。存储环节要看验证码存在哪里、key怎么设计的是按手机号加业务场景存还是所有验证码塞在一个全局集合里这直接决定了后续绑定关系能不能成立。校验环节是逻辑缺陷最集中的地方。先查验证码还是先查用户、是查存在记录还是查唯一一条记录、允不允许无限次尝试每一步都可能被钻空子。失效环节则要确认有效期多长、使用后有没有立即标记失效、换个手机号再提交旧验证码还能不能用。这一点很多开发会忽略验证码校验成功后记录状态没更新同一个验证码能反复用等于给攻击者留了一扇没关的后门。这些环节的毛病不是独立的经常连环出现。“四位验证码”本身只是空间小真正导致被利用的是校验和失效环节没有补上对应的限制。用个接地气的类比验证码就像一把密码锁四位数字就是只有10000种齿形的锁芯如果允许人无限次去试开锁只是时间问题如果更惨一点锁试过了还不更新齿形那就连“换锁”这个动作都省了这把锁形同虚设。1.2 用数字说话四位验证码为什么经不起“无限制”测试单看四位数字组合空间是 10^4 10000 种这个数字在安全设计里属于“很小”的规模。假设一个校验请求从发出到收到响应耗时0.2秒单线程遍历全部组合理论上大约需要2000秒也就是半小时左右如果网络和接口响应再快一点或者分成五个线程并行跑只需要几分钟。而一般短信验证码的有效期是5到10分钟时间窗口完全够用甚至绰绰有余。这里还要考虑一个现实因素验证码校验接口通常不是每秒只能处理一个请求。某些场景下校验接口前面没有限流或者限流只存在于前端按钮的倒计时后端直接接收请求用脚本循环提交就行。10000次请求对任何后端来说都是很小的量日志都不会有明显异常但验证码已经可以被试出来。所以结论很直接四位验证码能不能被打不在于位数而在于校验环节有没有做“次数限制”“时间锁定”“会话绑定”这三件事。这三件只要缺一个漏洞基本就成立了。也正因为这个原因验证码逻辑缺陷在edusrc这类平台上常年处于高发状态不夸张地说只要肯花时间把验证码全流程认真过一遍大概率会有收获。2. 在edusrc场景中高频出现的几类验证码逻辑缺陷2.1 发送接口失控短信轰炸的口子先说一个非常常见的获取验证码的接口本身没有任何频率控制允许同一个手机号在短时间重复请求几十次、上百次短信通道就会一直下发。这种问题在edusrc的低危漏洞里出现频率极高很多新人对SRC的第一条有效提交就是这类。测试思路很简单就是在授权范围内用同一个测试手机号连点“获取验证码”观察是否每次都正常收到短信。如果每次都收到说明后端没有做同号限制如果换着不同的场景注册、登录、找回密码分别请求每个场景都能给同一个号发一条也算一种变体。有的系统做了IP维度限制但没做手机号维度限制换个号码照样能刷这也是需要关注的点。修复也比较直接同一手机号60秒内只能发送一条24小时内同一个号设置发送上限比如10条必要的时候在发送前加图形验证码或滑块让自动化脚本无法直接触发短信通道。这里说的图形验证码是防止刷接口的手段和短信验证码本身是两层东西不要混淆。测试时尤其注意如果前端已经做了60秒倒计时一定要试着绕过前端直接重放发送请求因为前端限制不等于后端限制这类接口在后端裸奔的情况太常见了。2.2 校验接口无次数上限四位数被遍历的前提如果发送接口不限频是低危那么校验接口不限次就是中危甚至高危。因为不限次直接给了攻击者遍历四位验证码的可能这也是我强烈建议测试人员在验证码逻辑测试里重点去看的一个环节。判断方法很直观提交一个错误的验证码接口返回“验证码错误”连续提交10次、50次、100次依然返回“验证码错误”并且没有出现“尝试次数过多”“账号已锁定”之类的提示那么这个校验接口就是无上限的。有了无上限的校验接口攻击者只需要一个自动化脚本循环提交0000到9999。在业务层面表现为大量错误请求但在授权测试中这就是确认漏洞的必要操作。# 仅用于授权测试环境的概念性伪代码 for i in range(10000): code f{i:04d} result submit_code(mobile13800138000, codecode) if result.status success: print(f正确验证码: {code}) break修复方向是同一个手机号、用户或IP在单位时间内校验失败次数达到阈值通常取5到10次直接锁定一段时间验证码在N次错误后立即失效需要重新获取。锁定期间再提交任何验证码都返回同样的错误提示避免攻击者通过响应差异继续判断验证码是否正确。2.3 校验顺序不当账号枚举从验证码接口“侧漏”有些系统的校验接口处理顺序是先校验验证码再校验用户是否存在。这就带来一个很有意思的逻辑差输入一个错误的验证码但手机号也不存在接口返回“验证码错误”输入一个正确的验证码但手机号不存在接口返回“用户不存在”或“手机号未注册”。攻击者只需要先想办法搞到一个正确的验证码比如用自己手机号接收然后拿着这个验证码去枚举手机号就能区分哪些号注册过哪些没有。这种问题的本质是把两件不相干的事放在了一个接口里而且顺序选错了。正确的做法是先校验用户或账号是否存在再校验验证码是否正确并且无论哪一步失败返回给前端的都统一成“验证码错误或账号不存在”之类的话术不暴露具体是哪一项失败。测试时可以这样验证先用自己的手机号正常获取一个验证码然后在目标系统中找一个疑似未注册的号用同一个验证码去提交观察两者返回的提示文案是否有差异。有差异说明逻辑顺序有问题没有差异说明这块处理得还算严谨。这个测试方法成本很低但很多测试者会因为“验证码明明是对的”而忽略换号验证这一步。2.4 验证码与会话解耦一个验证码全家通用的由来再往深了看一个更隐蔽但危害更大的问题是验证码没有和手机号绑定。有的业务把验证码存在一个全局集合里key是随机生成的uuid而不是手机号加业务场景校验时只对比这串数字对不对完全不看是发给谁的。这就导致A手机收到的验证码B手机在提交时也能通过。这种问题在edusrc的实际案例里并不少见。测试里常见的情况是用测试手机号A获取验证码然后在另一个完全不相关的手机号B的找回密码流程里输入A收到的那个验证码居然能继续往下走。这已经不只是“爆破”问题了相当于验证码变成了一把万能钥匙只要知道任意一个人收到的验证码就能在所有账号上使用。另外还有一类变体同一个手机号在“注册”场景获取的验证码拿到“找回密码”场景去用也能通过。这是没有把“业务场景”作为验证码用途维度的一部分来做区分。如果验证码和用户身份的绑定关系再缺失严重时甚至可以直接接管任意账号。测试时建议关注三件事验证码是否和手机号绑定、是否和当前会话绑定、是否和业务场景绑定。三个维度能单独区分开这个接口才算稍微靠谱一点。2.5 并发与条件竞争绕过频率限制的野路子有些系统其实做了限制比如“同一个手机号60秒内不允许重复发送”用普通方式单线程重复请求确实会被拦下。但有些实现是先查询“最近有没有发送记录”再插入新的发送记录这两步之间没有加锁或者没有唯一性约束导致并发请求能同时通过查询判断进而绕过限制。测试时可以用并发工具同时对发送接口发多个请求比如一次并发10个请求如果后端只允许60秒一条但实际收到了多条短信就说明判断逻辑存在竞争窗口。这个问题在验证码相关接口里不算少尤其是自研系统里面很多开发没有意识到“查询后再写入”这个动作不是原子操作。修复方案通常是在数据库层面对“手机号时间窗口”做唯一性约束或者把发送记录的查询和写入放到同一个事务里加锁再高级一点就是引入分布式锁确保同一手机号的发送请求串行化。这类问题在报告里写清楚触发条件和并发量级平台审核通常都会认可毕竟它绕过的是设计层面本应成立的限制。2.6 响应内容泄露接口把验证码“送”到前端还有一种让人哭笑不得的情况发送验证码的接口在HTTP响应报文里直接把验证码明文返回了比如JSON里带了一个code: 1234或smsCode: 1234。这种问题多出现在开发调试阶段开发为了方便在联调环境里直接返回验证码然而上线时忘了删。测试时只需要打开抓包代理正常触发一次“获取验证码”逐条查看响应报文即可。一旦发现接口返回里带验证码明文这是非常典型的信息泄露在edusrc这类平台里通常可以按中危或高危提交因为它等于完全绕过了验证码机制。还有一类更隐性的泄露是验证码不直接返回但返回了一个可预测的“验证码标识”。比如接口返回verifyId: 123456而这个id是按顺序递增的后续校验时要求同时提交验证码和这个verifyId攻击者可以遍历verifyId和四位验证码的组合本质上没有提高安全强度。这类问题的排查成本极低但很多测试新手会忽略。我习惯在测试任何验证码接口时先顺手看一眼发送接口的响应体数值字段、嵌套对象、调试信息字段都扫一遍十分钟就能排除掉一大类漏洞。3. 可复用的验证码逻辑测试流程3.1 测试前提授权、环境与身份边界无论测试思路多么清晰第一条永远是有边界。在edusrc或者任何SRC平台做测试都必须在平台允许的范围和规则内进行如果是团队授权测试也要确保拿到书面的测试范围和授权书。不要用别人真实的手机号去做短信轰炸验证不要在生产环境做大量遍历请求以免影响正常业务尽量准备专门的测试手机号或者使用平台提供的测试账号。一些SRC平台对短信接口有特殊的测试时间要求比如要求在工作时间测试、要求控制请求量级这些规则在测试前一定要读一遍。宁可少测也不要因为一个低危漏洞把自己送上封号边缘。另外测试过程中产生的请求日志、验证码记录、短信记录建议测试结束之后及时清理不要留在业务系统里。3.2 先用代理把验证码全流程“走一遍”开始正式测试之前我会先打开Burp Suite这类抓包代理工具在目标系统里把一条正常的验证码流程完整走一遍点击获取验证码填写收到的验证码点击提交观察整个流程里的所有请求和响应。这一遍不是找问题而是建立对系统的“正常心智模型”。重点记录几个信息发送验证码的接口URL、请求方法、参数名一般是mobile、phone、scene、type之类校验验证码的接口URL、参数名一般是mobile、code、verifyId提交成功和失败时返回的HTTP状态码和JSON文案。把正常流量存成一个请求包后续所有测试都围绕这几个接口展开。这里有个容易被新手忽略的细节发送验证码的接口和校验验证码的接口不一定是同一个域名或路径。有的系统把发送接口放在网关后面校验接口在业务服务里有的系统则是前端直接调用第三方短信服务商的接口再由服务商回调。前者需要把所有相关接口都找出来后者则要关注回调是否校验签名、是否校验来源IP如果回调接口可以被伪造那验证码记录本身都可能被污染。3.3 拿着这张清单逐项排查根据上面的分析我在实际测试时会把以下项目列成一个清单逐个打钩。你可以直接复制这份思路作为自己的模板。发送接口同一手机号短时间能否重复发送多次发送接口验证码是否与手机号、会话、业务场景绑定校验接口错误验证码尝试次数有没有上限有没有锁定机制校验接口校验顺序是先验验证码还是先验账号提示信息是否有差异校验接口正确验证码用在不同账号或不同场景上是否也能通过失效机制验证码有效期多长使用一次后是否还能再次使用返回内容发送、校验、重置密码接口的响应里是否含验证码或可预测标识并发场景多个并发请求能否绕过发送频率限制防自动化发送验证码前有没有图形验证码、滑块或行为校验每一项测试都可以在代理工具里构造相应请求来完成。每测完一项建议记录下请求包、响应包和结论因为这些记录最后都会变成漏洞报告里的证据链。不要凭记忆写报告时间一长很容易记混哪个接口测过、哪个没测过。3.4 一个典型漏洞案例的完整推演拿我这阵子遇到的那个“找回密码”场景举例把完整过程推演一遍。目标是一个高校身份认证系统的找回密码功能测试前已经确认授权范围内使用的是平台提供的测试手机号。第一步正常请求打开代理点击“获取验证码”发送接口是/auth/sendSmsCode参数是手机号和场景值随后在短信里收到四位验证码这一步没有发现问题。第二步看返回代理里查看发送接口的响应没有返回验证码明文这一步也没问题。第三步测发送限制用同一手机号连续点击“获取验证码”发现每点一次都能生成新的验证码并下发短信后端没有做同一手机号60秒内限制。这已经是一个独立的短信轰炸问题。第四步测校验限制输入错误的验证码连续提交20次接口每次都返回“验证码错误”没有锁定提示。这个信号非常关键四位验证码加上无限次校验基本可以确认遍历可行。第五步确认有效期和绑定查看验证码是十分钟有效然后尝试用测试手机号A收到的验证码在另一个测试手机号B的找回密码流程里提交发现依然能通过说明验证码没有和手机号绑定风险等级进一步拉高。第六步整理证据根据以上步骤整理出三个问题短信轰炸、校验无次数限制、验证码未绑定手机号。这三个问题叠加起来攻击者只需要在十分钟内遍历10000个组合就能拿到任意手机号的找回密码验证码从而绕过短信验证完成密码重置影响面覆盖所有注册用户。最终按高危等级提交了报告平台确认后也很快推动修复。4. 从攻防两端看验证码逻辑应该怎么加固4.1 设计原则验证码校验要服务化别各自为政把上面这些漏洞类型放在一起看会发现一个共性很多问题的根源是“每个业务各自实现一套验证码逻辑”注册、登录、找回密码、修改手机号每个功能都由不同的人负责各自写了一段验证码生成和校验代码防御水平参差不齐。往往某一个接口做了限制另一个接口却什么防护都没有。所以第一原则是验证码功能应该统一收口做成一个独立的验证码服务。生成、校验、失效、限流统一由该服务负责业务方只负责调用标准API。这样既减少重复开发也让安全能力能够集中加固。至少要在团队内沉淀一套统一的验证码SDK不允许业务代码自己手写随机数生成和比对逻辑。第二原则是验证码永远只是一道“门槛”不能是“门后面的锁”。真正决定账号安全的是后续的会话管理、风控体系。在敏感操作上加双因子、在异常行为上做风控这些应作为更高层级的防御手段。很多业务把验证码安全当成唯一防线一旦验证码被绕过整个账号体系就直接裸奔这是设计上的根本问题。4.2 每一条具体缺口对应的加固方案把第2章里的几类问题和对应的修复措施整理成一张对应关系方便开发和安全人员对照自查。问题类型具体现象加固方案验证方式短信轰炸同一手机号短时间多次收到短信60秒发送间隔、单号24小时上限、前置图形验证码授权下发一条后立即再点观察是否拦截校验无限制错误验证码可无限尝试失败5次锁定15分钟、错误次数触发验证码自动失效连续提交错误码观察是否出现锁定提示校验顺序错误提示文案暴露账号是否存在先校验用户存在性再校验验证码文案统一用不同号码对比返回文案验证码解耦A号验证码B号可用校验时强制带上手机号、场景、会话ID后端严格匹配交叉换号测试验证码未失效使用后仍可重复提交校验成功后立即置为已使用幂等处理同一验证码提交两次响应泄露接口返回验证码明文全链路排查响应报文禁止输出验证码代理抓包逐条查看并发绕过并发请求绕开发送频率限制数据库唯一约束或分布式锁并发请求模拟预测性标识verifyId可枚举改为UUID或带签名的随机令牌观察返回字段是否递增这里想额外强调一点加固不是“加上限制”就完事了还要保证限制本身的逻辑不产生新的漏洞。比如锁定手机号15分钟如果攻击者故意对受害者手机号触发多次错误尝试就能导致受害者无法正常使用找回密码功能这本身又变成一种“故意的拒绝服务”。所以锁定时长和触发次数要选择一个平衡值同时最好只锁定验证码校验这个动作不要锁定用户的整个账号登录能力。5. 常见问题与实践心得5.1 测试中容易白忙活的几个误区先泼几盆冷水。第一个误区以为前端倒计时就是后端限制。很多前端按钮有60秒倒计时但把代理拦截下来直接重放发送请求后端照样放行。前端限制只是体验优化不是安全控制一切以后端接口行为为准。第二个误区只测验证码本身不测“验证码账号”的组合绑定关系。很多人拿到验证码后只验证“这个验证码能不能通过校验”却忘了换一个手机号、换一个业务场景再试一次。绑定关系缺失往往是在交叉测试里才暴露出来的这部分测试不花什么时间却经常能挖出比爆破更严重的问题。第三个误区用多个自己的手机号去测试短信轰炸。既浪费真实号码资源又可能让运营商风控盯上你的号码。正确做法是用平台提供的测试号或者同一个测试号测少量几次就足够判断。第四个误区校验接口的错误返回只看状态码不看响应正文。有些系统把错误信息放在响应body里不同错误文案本身就是信息泄露状态码全是200不代表逻辑正常。我见过不少系统状态码恒为200所有判断逻辑都藏在JSON的message字段里不看正文等于白测。第五个误区遍历验证码时没有控制速度直接把业务压挂。授权测试也要注意量级先算好时间窗口单线程尝试必要时分段进行不影响正常业务使用。真要验证是否可遍历不需要跑完10000个组合只要确认“错误次数无限制且验证码有效期内可持续提交”这两个前提就可以在报告里说明漏洞成立同时附上小规模的实测记录即可。5.2 写漏洞报告时特别值得注意的几点edusrc平台对报告质量的要求越来越高一份平庸的报告和一份优秀报告的区别通常在细节。标题要直接点出影响比如“某系统找回密码接口验证码未绑定手机号且可遍历导致任意账号密码重置”而不是笼统写“验证码逻辑缺陷”。复现步骤要按时间线写清楚每一个操作配上请求地址、参数和返回结果截图。危害描述要先说最坏情况比如“可导致任意用户账号被接管”再说影响范围和前置条件。修复建议要有可落地性直接给到业务方可以改的方向。还有一个小技巧报告里把计算过程写上。比如“验证码有效期10分钟四位数组合共10000种单线程遍历约需要2000秒在有效期内可以跑完”这个计算过程会让漏洞的严重性变得非常具体平台审核人员和业务方都能一眼看懂。另外别忘了在报告里说明测试时使用的手机号和大致请求量级遵守平台的测试规范。报告质量很大程度上就是你个人专业度的体现这也是后续在edusrc积累积分和排名的基础。如果现在你刚好在edusrc上做验证码方向的测试我建议先从发送和校验两个接口开始把绑定关系、失效机制、响应内容都过一遍通常不会空手而归。四位数验证码本身不是原罪原罪永远是“可以随便试”和“试完还能接着用”这两件事。
返回列表