查看文章 |
登录系统中验证码的使用(防注册机SPAM)
2008-07-16 11:33
登录系统中防止暴力破解账号 ,很有效的一个办法就是使用验证码(这里所说的都是图片的验证码),我一直以来也都是使用这种办法,现在来探讨一下关于验证码的实现方式以及一些弊端和解决办法。 一个基本的验证码方案实现流程为: 1、实现一个图片生成模块,然后用此模块实现一个输出图片的页面。这个页面每次每访问时都需要生成一个新的验证码,把验证码的值存入一个能跨请求(Request)的存储系统中(比如Session),然后输出这个验证码图片。 2、在需要加验证码的页面引用1中的验证码图片地址(<img src='xxxxxxx' />)。在每次用户数据提交后如果页面不刷新,也需要重新加载验证码图片(即刷新验证码)。 3、用户提交数据后,后台系统中拿到用户提交的验证码和存储系统中的验证码值作比较,如果不匹配则报错(这时候需页面需要生成新的验证码)。 这样一个流程基本能实现一个简单的验证码方案,不过这种方案对用户体验却有个弊端:用户每次都得输入验证码,验证码越难认,输入越麻烦(我有过10几分钟没有完成yahoo注册的经验,其中一个问题就在于那个验证码太复杂,输错验证码后填写的注册信息全丢失)。如果你尝试登录Google,在N(10)次登录不成功之后,登录页就会出现一个验证码,之后的登录尝试都必须填入验证码。 我现在分析怎样改进前面的方案,实现和Google登录系统一样的验证码效果(如果你的验证码是每次都需要的,则用不着下面的方法)。 如果需要等到登录失败多次后再出验证码,那么需要在Session中存储一个登录次数,每次登录失败后都需要把次数追加。当失败次数达到临界值(N)时,登录页面就需要呈现验证码,这时候验证码的生成和提交就可以参照上面的步骤。在这种方案下,每次数据提交的时候都需要检查当前登录次数是否达到临界值,如果达到则验证用户提交上来的验证码,否则就不用验证。 系统设计到这里,问题并没有完全解决,它还不能真正阻挡注册机。如果用Session存储验证码和失败次数,由于实现实际系统中的SessionID基本都是存在Cookie中,如果某个注册机访问你的页面后总是不发送Cookie(这时候SessionID也就不存在),这样Server端每次请求都产生新的Session,这样登录次数永远达不到临界值,也就不用验证码,绕过了访问验证。怎么解决这个问题?看下面分析。 上面的那个安全漏洞产生的原因就是由于Session的实现机制导致:客户端请求时不发送SessionID,服务器就只能产生新的SessionID,“自动产生”是问题所在。当然这个问题并不是说Session机制的不好,只是在这里不适合罢了。我们需要自己模拟用户登录会话(Login Session),实现的方式如下: 访问登录页面时,首先需要生成用户登录会话凭证(LoginToken,相当于SessionID,要随机,不能被枚举,要长点),然后把这个LoginToken输出到页面的Hidden字段中。同时用这个LoginToken作为key,验证码和登录次数作为value,把他们存储在类似memcached这样的外部存储系统中。当用户提交数据时同时提交这个LoginToken,服务器端根据这个LoginToken来获取相关的登录尝试次数和验证码进行比较,如果提交数据时丢失LoginToken或者LoginToken不存在则肯定为非法提交。 这个问题是最近在面试中被问到的,当时回答有点乱,但大致思路不差,现在做一下总结。 更新:下面的讨论对此方案有所补充。 |
最近读者: