OpenResty实现验证码服务

2014-05-22

   我们的游戏正在重构好几个重要的服务模块。首先要做的就是重构登陆服务,登陆服务其中一个很重要的防止机器人的方法就是加入验证码,虽然有人能够破解一些简单的验证码技术,但是,一个简单的措施就能阻挡大部分的恶意攻击,这还是很划算的。我之前接触到的项目大多使用PHP或者Java来实现验证码功能,但是,把它作为登陆服务的一部分,有点不大合适。它本就可以作为一个单独的小型服务存在。所以,经过和领导商议后,决定还是使用OpenResty + Redis来实现这个独立的验证码服务。以后,其他的任何流程都能使用它。
   验证码的流程还是很简单的。核心就是:在服务端按照指定文字(符号、数字等)生成一些只有人才能识别的模式(图片或者声音),把生成的东西和文字一一对应起来。把人识别的结果发送会服务端进行文字比较,即可完成验证。
   需要考虑几个问题:

  1. 验证码有过期时间限制,一般设置为三五分钟即可。
  2. 如果验证码数量很少,攻击者暴力记录,那么验证码就没有意义了。所以,验证码集合一定要大些。
  3. 若验证码集合大,则如何防止攻击者而已生成巨量的验证码,导致验证码服务瘫痪?
  4. 若验证码集合大,服务端如何管理图片巨量的验证码图片?
  5. 若缓存验证码图片,图片URL持久不变,会导致安全性问题。若不缓存,则必然需要定期清理图片的功能。

   第一个问题,因为Redis 可以给key-value 对设置过期时间,很容易就实现了。
   第二个问题,
4 位 数字                 =>   10000    图
4 位 数字 +  字母    =>   36^4  图 ~ 170w 图
6 位   数字 +  字母       36 ^6    21 7678 2336L  基本上就不需要缓存了
   所以,至少应该选择后面两种方式。选择自增的captchaid,或者UUID的captchaid,也是为了降低攻击者把capthaid 与captchavalue  手动关联起来的可能性。
   第三个问题,验证码服务有两个接口:申请验证码,验证验证码。申请可以由登录服务器,或者client完成。若是由登陆服务器完成,验证码服务则可以做成内网的服务;若是由client完成,验证码服务器就可能暴露到广域网,就需要在验证码服务中增加恶意请求识别、过滤的功能,和登陆服务器就功能重复了。但是,验证码服务不涉及到关键信息泄露,最严重也只是会导致服务崩溃而已。自行斟酌了。
   第四个问题,经测试,生成的图片虽然大小很小,4KB左右,但是,如果选择4位字符,总容量会占到7GB,若采用6位字符,图片总容量会占到9000GB,这可能就有问题了,21亿的文件数能否处理都是一个问题。我们就简单的采用4位字符,也不用定期清理磁盘了。
   第五个问题,因为,若攻击者把所有固定URL 的captcha 图片导出了,他在某个网站上把这些验证码挂出去,让成千上万的网民帮他人肉破解验证码,4位长度的验证码很容易就搞定了。所以需要隐藏真正图片URL。当不缓存图片时,就不需要考虑了。

  captcha 验证过程

  1. 随机生成一个4~6 位数、字母作为captchavalue,需要一个自增ID 作为captchaid,  存入redis (  set captcha:[id]  captchavalue  )
  2. 用Lua-GD 根据captchavalue 生成图片,存到图片目录。把生成的图片url,captchaid传给 client,  
  3. client HTTP 提交 captchaid, captchaStr ,login server 到redis 数据库查询captchaid获取captchavalue ,比较captchavalue 、captchaStr
  4. 验证其他步骤

根据symbol生成图片,还是需要靠GD库,Lua语言有GD库的绑定,luarocks直接安装即可,还需要UUID 库。
我写了一个python脚本,多进程的模拟压力测试。经测试,37s,10w次 request,性能肯定不是问题。

 

由于软件或者lib的更新都还是很快的,这里就不列出安装过程了,可参考下面的文档。

http://blog.javachen.com/2014/04/01/deploy-a-captcha-server-using-lua-and-openresty.html

如果有任何意见,欢迎留言讨论。


[ 主页 ]
COMMENTS
POST A COMMENT

(optional)



(optional)