一种低成本的找回密码token验证方案

随着互联网的高速发展,WEB2.0网站的业务越来越庞大,一些token验证在许多场景下都必不可少,比如说交易订单的防止多次提交,重要的敏感操作防止CSRF(跨站请求伪造)攻击,以及短信验证码,找回密码验证码,注册登录图形的生成和验证。
token的特点主要有如下几个:

  1. 唯一性

  2. 时效性

  3. 不可预测

很多大型业务中,比如说BAT的找回密码流程中,对于发给用户的找回密码链接邮件需要同时提交用户输入的vcode验证码和vcode_md5也就是该校验码对应的token。很多人认为这时需要一个缓存中间件比如说Redis或者Memcache来存储校验码对应的需要重置密码的用户Uid。其实大可不必如此。

我们可以在服务器端使用rand()生成随机数,然后将生成的随机数加上指定的salt做md5处理,比如说

function vcode($uid) {
    $output['vcode_timestamp'] = time();
    $output['vcode'] = $uid;
    $output['vcode_md5'] = md5("changwei"+vcode_timestamp+$uid);
    return $output;
}

生成的时候将vcode和vcode_md5一起通过邮件发回给客户。

那么验证的时候该怎么验证呢?很好办,把指定的盐值加上校验码以及其他附带信息(比如说用户UID)进行md5运算,和客户端提交的那个事先发给客户端的vcode_md5进行比较,如果一致那么就通过。

那么我们来看看这是否具有token的几个特点呢?首先,唯一性,可以通过uid(这个一般是用户表的递增主键,不会重复),并且将当前时间戳作为随机数种子,基本上可以认为是高度随机的数字了,加上md5加盐运算之后客户有视为高度近似的唯一性了。不可预测性呢?由于指定的salt是存储在服务端,只要这个salt足够复杂而且没有被泄漏,那么这个vcode_md5肯定是无法被客户端预测的,也保证了这个方案的安全性。

最后就是时效性了,很多人总在纠结,如果没有Redis或Memcache,那么如何去存储他的时效性呢?通过cookie吗?不行,cookie是在客户端的,那么意味着是用户可控的,不安全。用session呢?标题写了是低成本解决方案,session存储在服务端,在高并发请求数量庞大的大型网站中,每时每刻都有成千上万的校验码生成,如果都存在session对于服务器压力(尤其是IO)是十分大的。那么还有什么办法呢?我们想想,既然md5是消息摘要算法,那么他就可以用来验证数据在传输中途是否经过了篡改,那么思路就来了,我们在发回给用户的表单中再加一个隐藏域,名字为vcode_timestamp,后端算法再改一改,这次提供完整的验证算法源代码。

function vcode($uid) {
    $output['vcode_timestamp'] = time();
    $output['vcode'] = $uid;
    $output['vcode_md5'] = md5("changwei"+vcode_timestamp+$uid);
    return $output;
}
function verify() {
    if(md5("changwei"+$_POST['vcode_timestamp']+$_POST['uid'])===$_POST['vcode_md5']) {
        if(time()<intval($_POST['vcode_timestamp'])+60*60){ // 60*60表示时效性为一个小时
                return true;
            }
        return false;
        }
    return false;
}

这样一来时效性也可以验证了,全过程没有涉及到任何服务器端的存储引擎,可以说是把成本降到了最低。而且md5本身也是目前效率非常高的hash算法,对于cpu的消耗也不是很大。

目前我的网站lol.changwei.me

中取消监控链接便用的是该方案,经过上线一个月的测试,基本上没有出现什么BUG。

    原文作者:昌维
    原文地址: https://segmentfault.com/a/1190000006011935
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞