@
aec4d 4 位数字, 1000 次就能破,你怎么得出这个结论的?
我没有说不限制错误次数,但依赖的不是 IP 或者 hash 或者加密的 cookie ,
而是记录请求获取验证码时的 phone number 或者 email address 就可以限制了,
验证的最终目的是验证 phone number 或 email address 的有效性。
--------------------------------------------------------------------------------
根据回帖看,大家热情似乎不高啊,以下分享一下我的解决方案吧:
1.先说一下非存储型验证码的目的和动机:
之所以不希望存储类型的验证码,一来是想减少程序对外部资源的依赖,不管存储到 file 或是 redis ,这样始终会有额外的开销,需要维护额外的业务,减少对环境的依赖,这样对于后端分布式,去中心化的部署会很轻
2.不过是非存储型验证码还是存储型验证码,对试错的次数肯定需要限制的,例如,在 N 时间内连续输入 N 次,暂停向该用户开放功能权限(登录,注册,找回密码)
3.具体解决方案:
1) 首先随机生成一组 hash 值(用于后面作为 hash 的 salt )
2) 然后获取用户输入的 phone number 或 email address
3) 设置验证码有效期,这里默认为 1 小时
4) 获取时间 Y-m-d H-1 , Y-m-d H , Y-m-d H+1 生成三个时间,分别为当前时间(小时)的上一个小时,分别为当前时间(小时),分别为当前时间(小时)的下一个小时,具体为什么这样做,请看后面
创建一个迭代,使用 md5 hash
for timeItem in timeContainer:
for hash in hashSet:
codes[] = md5(hash, phone or email, timeItem) // 如果需要数字验证码则可能需要在这里过滤一下,在装到 codes
最终所有的验证码都存储到 codes ,然后随机取一个 code 作为验证码给客户端就可以了,
当客户端提交数据用于验证“验证码”是否正确的时候,继续用上面的参数生成所有的验证码,判断提交的验证码是否在 codes 里面,如果在里面就表示是有效的,否则无效。
有人可能会说这个验证码可以重复使用,没错,在一个小时内是可以重复使用的,但验证码的目的就是验证 phone number 或 email address 的有效性,所以这不是什么问题。
然后就是对试错次数的限制,如果不限制试错次数,那么验证码基本没有任何意义,尤其是 4 位数字验证码,运气差一点 9000 多次也差不多了,所以需要限制试错次数,这个不需要我再说了,大家都知道怎么做。
上面提到的三个时间,上一小时,当前小时,下一小时,之所以这样是为了防止,用户在 23 : 59 : 59 秒获取的验证码,而提交的时候是第二天了,计算不出验证码。
最后,也许你有更好的解决方案,请不吝赐教,如果以上内容有改进或任何问题,请帮助指出,谢谢。