SecMap - 验证码
SecMap - 验证码
功能点介绍
全自动区分计算机和人类的图灵测试(Completely Automated Public Turing test to tell Computers and Humans Apart,简称 CAPTCHA),验证码,作为区分人与机器的经典手段,安全中有着特殊的地位。作为辅助的安全手段,和 Web 中的其他漏洞相比似乎微不足道,但如果能绕过验证码,则可以把手动变为自动,对于攻击者也是扫清了一个大障碍。
验证码常见的有纯文本验证码、图形验证码、行为验证码:
- 纯文本验证码:比如
今天是星期几?
、75 * 2 = ?
,这种验证码很容易破解,比如对于四则运算,攻击者只需要在 html 中用正则拿一下问题就可以直接算结果了,所以现在已经非常少见了,基本上可以被秒杀。 - 图形验证码:现在非常多网站采用的就是这种,主要的思路就是“用户识别”,有简单的纯数字、加英文字母、加各种干扰线、加噪点、Apple 会动的验证码、Google/12306 选物体的验证码,难度依次递增。图形验证码是对抗历史最为悠久的一种,攻击者利用各种算法识别验证码,甚至动用了机器学习/深度学习,肯下成本的还用了人工打码平台。总体而言,对于攻击者来说成本不算高。
- 声音验证码:有些验证码为了提高用户体验和照顾视觉障碍的用户,会用声音验证码,一般来说是机器生成一段读数字的语音,然后让用户识别,有些是给你打电话,有些是直接网页上就可以听语音。由于攻击者要识别(有时候需要接管通话)语音,所以相对来说成本高了一些。
- 行为验证码:总之就是思路从验证“用户识别”变成了验证“用户操作”,比如有滑动验证码、拼图验证码、还有各种诸如“投篮”等奇葩的验证码。比较有代表性的就是极验的滑动验证码,比较有名,很多厂都在用。滑动验证码不但不是很好模拟,而且往往加入了生物特征识别,比较难自动化,对于攻击者来说成本比较高。
攻击思路
客户端生成验证码
验证码由客户端生成,比如用 js,在客户端验证,可以直接伪造数据包绕过;或者发回给服务端校验,修改响应就轻易可以绕过验证码。
修复方法:验证码的生成、比对应该放在服务端进行。
验证码回显在响应中
有些程序会将验证码放在响应中(body、Set-Cookie),这个时候只要拦截发送的请求,查看响应就可以获得验证码了。
修复方法:避免将验证码回显在响应包中,验证的比对应该放在服务端进行。
验证码可以为空
后端没有考虑到验证码为空的情况,不传验证码可以直接通过验证码的认证。ecshop 曾经出现过此问题。
修复方法:后端的逻辑应该是,没有收到验证码,等同于验证码错误。
万能验证码
一般是开发人员测试时所用的测试验证码直接部署在线上,不用调用任何获取验证码请求的接口,直接输入该万能验证码,即可通过验证。
修复方法:上线之前去除所有测试、调试逻辑。
验证码可复用
对于一个 session,验证码可以重复使用,攻击者只需要保证 cookie 没变,这个验证码就基本没有用了。
修复方法:一个 session 的验证码,一旦使用过了就应该销毁。
验证码可被自动识别
最常见的,就是验证码图像过于简单,比如没有任何干扰线/扭曲的验证码。但是本文不打算讲验证码的识别方式,网上有很多资料,橘友们如果感兴趣的话可以看看这一篇:https://wooyun.x10sec.org/static/drops/tips-141.html
还有一类是,验证码图像复杂,但是存在其他特征可以间接识别。一个经典案例是:https://segmentfault.com/a/1190000021903141 。这个验证码其虽然不好直接识别,但是可以利用 svg path 长度这一特征,将其映射到特定验证码图片的字符上,遇到特定长度的 svg path 就等于见到了特定的字符,那么就可以自动化识别了。拓展一下思路,类似的问题可能是:返回验证码的数据包大小存在特征、验证码字符固定的颜色...
修复方法:1. 保证验证码图像本身其复杂度(添加英文字母或者汉字、扭曲字体、加干扰线、加噪点、粘连,有必要可以上行为验证码);2. 实现的时候注意验证码是否有其他特征可以间接识别出每个字符。
验证码可暴力破解
- 验证码提交错误之后,后端没有把对应 session 的验证码重置,导致攻击者可以重复尝试同一个验证码,导致验证码可暴力破解
- 验证码所有的可能性应该是很大的,这样可以避免攻击者重复提交一个答案绕过。但如果验证码本质上是在做选择题(通常只有 A、B、C、D 四个选项),那么所有的可能性就只有 4 种。案例:
wooyun-2013-025245
。
修复方法:1. 一个 session 的验证码,一旦错误就应该销毁;2. 提升用户体验的时候,也要时刻注意验证码的空间大小,防止空间缩小导致验证码容易破解。
验证码功能开关可控
后端有关闭验证码的功能,但这个启用的变量可以由前端传递参数控制。为什么会有这种情况呢?可能是调试功能没有下掉;或者,有些验证码接口是 A 部门统一提供的,然后某个业务方用的某个前端框架是已经直接封装好了验证码功能,但是实际上又不需要使用它(比如内部的某个不重要的系统),就可能要求验证码接口有个关闭验证的参数。
案例:wooyun-2014-071289
、wooyun-2013-026219
、wooyun-2012-014563
修复方法:没有必要就别设置开关了;一定要设置开关的话,不能由前端控制;非要用前端控制的话。。。参数值加个密吧,但这个控制方式一定只能放在内网的系统上使用。
DDoS
有些验证码的实现是,前端传参中含有证码图片的大小,后端根据这个参数来生成验证码图片;加上后端没有限制验证码图片的大小,那么攻击者只需要生成超大的图形验证码,就很容易造成内存耗尽,达到拒绝服务的目的。
修复方法:验证码生成的一切逻辑应在后端完成;如考虑服务兼容性一定要接受前端传参时,也要对其进行大小限制。
验证流程的顺序有误
验证码在验证的时候,顺序/逻辑关系不正确,导致验证码失效。
如:先校验用户名和密码,然后再验证验证码是否正确,这明显逻辑顺序有误,验证码实际上没起到防护的作用。
修复方案:先校验验证码,再继续后面的业务逻辑。
总结
还是那句话,验证码更多时候是作为辅助的防御手段,简单却有效(虽然牺牲了一些用户体验)。验证码失效,一般不会直接带来安全问题,但是却会降低攻击者的攻击成本,导致出现安全问题的可能性增加。
攻与防,“成本” 是永恒的话题。
祝橘友们五一 “长” 假快乐
(其实就放了半天假)