安全 建议大家尽早应用验证码

chrisloong · 2015年01月22日 · 最后由 geekgao 回复于 2018年03月09日 · 14857 次阅读

之前一直认为,验证码是 web 应用中,体验最糟糕的环节,登录或注册账号时,最烦去识别那些验证码。 所以在开发产品的时候,一直没把验证码列入开发计划。虽然知道有隐患,但觉得我们增速不会太快,没必要那么早注重安全环节。

但是,最近审查产品的请求日志,发现有异样:

有很多请求,没有入口页面,上来就请求指定接口,并且请求 HEADER 信息一致。懂 web 的人肯定能看出,写个脚本,实现这种请求,非常容易。

在去年,抢火车票,抢小米,使得国内爆破 web 接口的技术水平,迈上新台阶。抢东西倒还好,碰撞密码才是大危害。 如果没验证码,那等于是虚掩着大门,开发者可以轻松推开;加了验证码,至少还有把锁,想进去的人,需要一些技巧,才能开。

这周在研究 CAPTCHA 实现方案时,发现这是 Ruby 的短板之一。 开源的方案中,大都是依赖 ImageMagick 这种第三方库去实现,验证码的图片很小,并不复杂,用个那么庞大的库,太浪费。

后面考虑用其它语言,包括 Java、C#,这类语言生成个验证码图片,很轻松,我们团队也都熟悉语法,但是运行时太庞大。 最后,考虑到 Golang,它的标准库带了图像处理,而且安装非常方便,解压下就能用,虽然不熟语法,但有静态语言经验,学起来不难,而且 Github 上有相关代码可供参考:https://github.com/dchest/captcha

在实现图片验证码前,仔细看过乌云的两篇文章:初探验证码识别常见验证码的弱点与验证码识别

不得不承认,国内的智能破解验证码水平,已经很高。所以在生成验证码图片时,不要太天真。 我们用的策略,就是尽可能随机,包括:背景色和字体色,字体倾斜角度,验证码位数等。

为了不影响常规体验,我们也是在登录失败达到一定次数后,强制用户输入验证码,这个次数统计,我们是放在 Redis 里面做,全局的。有些产品是把这个计数器,存在 Cookie 里,Cookie 是客户端传来的,不可信。

我们已经将这套方案上线:

简单说下我们的实现: 1) 提供独立的域名//captcha.ibanquan.com,来呈现验证码,这个域名的后端是 Golang 进程; 2) Ruby 中通过 Redis 记录一个账号的登录失败次数,一旦超过指定值,那么登录时必须带合法验证码; 3) 需要用到验证码时,前端发起请求://captcha.ibanquan.com/?callback=funcname(这里用 JSONP 避免跨域问题)返回:

func && func({"id":"8a362d95a1d5289b3429be8b936c9d31",
"path": "/image/8a362d95a1d5289b3429be8b936c9d31.png" });

4) 前端把上面返回的图片路径,放到域名后面: //captcha.ibanquan.com/image/8a362d95a1d5289b3429be8b936c9d31.png 作为标签的 src 就可以呈现验证码图片,再把 id 的值,隐藏在表单中; 5) 表单提交时,把用户输入的验证码,以及隐藏的验证码 ID 提交给 Ruby 后端; 6) Ruby 里面,通过验证码的 ID 从 Redis 取出对应 Value,和用户输入的验证码匹配。

随手点开社区的酷站,发现很多产品,要么没 CAPTCHA 机制,要么图片验证码太简单。 尤其是我们社区,都还没有 CAPTCHA 机制。其实开发者社区账号,更有价值,一旦开发者账号攻陷后,域名的账号、主机账号等都可以去碰撞,一旦得手,可以为所欲为。

最后,推荐一位国内安全领域牛人,微博 ringzero,乌云里核心白帽子猪猪侠

#1 楼 @Rei 通过频率检测,或黑白名单策略,还是不太可靠的。验证码是目前能想到的,最可靠的方案。 不过这个 gem 可以用来做预警,发现异常请求,就通知管理员。

直接上验证码感觉有点粗暴了,毕竟体验会下降不少。

  1. 上一个频率检测工具
  2. 表单开启 csrf, 能防止很多通用工具的攻击
  3. 必要的时候可以启用 客户端 JS 加密回传的方式来增强门槛。

#2 楼 @chrisloong 限制频率,10 秒一次,一天可以试 24 × 60 × 60 / 10 = 8640 次,一个 8 位小写字母的组合有 208827064576 个,全部试完要 24169799 天,已经足够了吧。

@Rei @lyfi2003 其实,通过频率限制,体验不一定能更好。实际人工点击,频率可能都会很小。如果输错一次密码,告诉我 10 秒后才能再试,我会不开心。之前 QQ 邮箱,就限制邮箱的收件刷新频率,向他们抱怨过,已改进了。 通过频率检测 + 验证码,可以既提升体验又加强安全。

@lyfi2003 之前我也觉得验证码粗暴。csrf 对于前端,只是多一步,先 get 页面,然后找到 csrf 的 token 就可以发请求。客户端加密回传,前端脚本代码是暴露的,一旦加密方法被破解,也就等于没有了。

#5 楼 @chrisloong 1 分钟 6 次,6 次失败之后提示频率太高,提示找回密码。

#6 楼 @Rei 嗯,网银和支付类系统就是类似,登录失败超过一定次数,直接锁定账号。但对离钱有点远的账号,这么做太严厉。

@chrisloong devise 有一个 lockable 模块和 timeout 模块,直接支持这种限制次数与锁定的模式。

mp.weixin.qq.com 的做法挺好的,登陆失败超过几次后就出现验证码。

顶顶,这个讨论有价值

#2 楼 @chrisloong 楼主你说了很多重要性,对实现讲得很少,只是用了这个程序吗?在 Rails 里面使用的时候有没有什么要注意的? https://github.com/dchest/captcha

谢谢

#9 楼 @flowerwrong 嗯,好的用户体验,都是这种模式了。我们也是这样。

#8 楼 @lyfi2003 如果要自己实现,也不难,还可以更简单。😄

#11 楼 @Peter 无关框架的,主要是自己造轮子,Golang 生成验证码和图片,Redis 存储数据,Ruby 验证数据。细节怎么做,看个人的了。 像我们是提供独立的域名,来呈现验证码,Server 端是 Golang 提供服务://captcha.ibanquan.com; 前端脚本用 JSONP 获取验证码的 ID 和图片地址,这个请求返回后,后端的 Redis 就有 ID 和验证码 Value 了; 表单提交时,把用户输入的验证码,以及隐藏的验证码 ID 提交给 Ruby 后端; Ruby 里面,可以通过 ID 从 Redis 取出 Value,和用户输入的验证码匹配。

#14 楼 @chrisloong

给客户端要求的话它爆破起来就慢很多了。

就这几个思路,很多系统都是结合着用,先限制,最后一步觉得用户还有风险才会启用验证码。

( ps: 我记得验证码在 google 的一个智能识别系统上被完破了 )

服务器出现了一点小问题!

#16 楼 @hooopo 哪里出了小问题?😱

#17 楼 @chrisloong 登录啊 想看看验证码。。。在哪里

#15 楼 @lyfi2003 嗯,对的。我们现在就是判断登录失败次数,超过了才会用验证码。 图片验证码,只要人容易认出的,程序要读取都不难。 人类那么聪明,发明了 CAPTCHA 来识别人和机器,结果又发明识别技术让 CAPTCHA 成为软肋。 难怪马斯克警告大家,要防范人工智能。👽

建议直接开启两步验证,步入未来时代

建议网站可以使用极验验证的验证码呢,基于行为式验证,能够有效的区别人和机器。目前就感觉这一种验证码反垃圾的作用最好。http://www.geetest.com/

传统验证码体验太渣,破解成本很低,随便一搜索网上一堆破解文章,随时会面临风险的,可以试试体验更加友好的同盾智能验证,安全性高体验佳 https://www.tongdun.cn/product/antiFraud/smartVerify

需要 登录 后方可回复, 如果你还没有账号请 注册新账号