zoukankan      html  css  js  c++  java
  • 短信验证码在 服务器 端的处理

    转载:http://blog.sina.com.cn/s/blog_80a6423d0102wm74.html

    目前,很多网站或app都要求用户用手机注册,比如滴滴打车的注册界面是这样的:

    论短信验证码在 服务器 端的最佳处理姿势

    流程大体分两步:

    1. 用户输入手机号,点击“获取验证码”(滴滴界面上叫“验证”),这时服务器会给用户的手机发送一条短信

    2. 用户查收短信后,输入短信验证码,点“注册”,服务器进行验证,如果正确,执行注册逻辑

    常规的服务器端处理流程

    1. 第一步,服务器生成一个四位随机码作为短信验证码,发短信出去,同时在数据库或redis里,记录下该手机号对应的这个验证码以及超时时间

    2. 第二步,用户输入验证码点“注册”后,服务器端在数据库或redis里取到上步记录的验证码,进行对比,如果相同,认证成功,继续后续业务处理

    大家可以看到,常规的服务器端处理,是需要操作数据库或redis的,如果数据库或redis挂掉,用户注册这个关键业务就没法进行了。 即使不挂掉,它们也可能成为性能瓶颈

    滴滴在《高可用架构》会场上分享了他们的实现方案:把方案做成无状态的,即,不依赖数据库或redis。 这是个很棒的思路,无状态带来的最好处多多(比如方便扩容、、)

    在会议现场提问环节,我提出了改进意见,由于时间仓促,没有与主持人深入交流,会后也因为私事匆匆离开,也没机会进一步交流

    下面是我的改进方案,不涉及短信防刷这类“无关”问题(因为无论哪种方案,都需要处理防刷)。 本质原理与滴滴的方案相同,全都是在非对称摘要算法上做文章

    第一步:用户输入手机号,点击“获取验证码”

    这时,http request包含了用户填写的手机号:

    phone= 18612345678

     


    服务器端,生成一个随机的四位码作为短信验证码(verify_code),发短信出去(这步略),为了方便,我用ruby代码表达,下同

    verify_code = " #{rand( 10 )} #{rand( 10 )} #{rand( 10 )} #{rand( 10 )} "

     


    算出过期时间exp(验证码5分钟后过期):

    exp = Time.now + 5.minutes

     


    假设服务器端有一个全局的SECRET(注意别泄漏):

    SECRET = "THIS_IS_A_SECRET"

     


    我们把这几项拼成一个大的string,算一个摘要值(token)出来:

    require ( 'digest' ) token = Digest::SHA256 .hexdigest(phone + verify_code + exp + SECRET )


    摘要算法我这里选了SHA256,可以根据情况调整


    在本次请求的http response中,把exp和token传回客户端,类似:

    {  " exp ": "2016-07-03 00:32:19 +0800" ,  " token ":"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
    }

    第二步:用户输入短信验证码,点“登录”

    这时,用户提交的http request中包含如下信息(在服务器端,我们用四个变量表示它们 ):

    phone= 18612345678
    verify_code_input= 用户填的短信验证码
    token= 上步传回的值
    exp= 上步传回的值

     

    服务器端先检查一下短信验证码是不是超过5分钟有效期了:

    if Time .parse(exp) < Time .now  halt( "短信验证码已失效,请重新获取" )
    end

     

    再检查用户输入的短信验证码是不是正确,算法跟上步一样:

    require ( 'digest' ) token2 = Digest::SHA256 .hexdigest(phone + verify_code_input + exp + SECRET )
       
    if token2 != token  halt( "短信验证码不正确,请重新获取" )
    end

    # 下面是验证通过后的代码了...

     

    这样整个验证过程就完成了

     

    附个流程图:

    论短信验证码在 服务器 端的最佳处理姿势

    后记:可行性和安全性

    攻击者因为手里没有SECRET,所以无法伪造token。 这一点保证了整个方案的可行性

    同样因为有SECRET,所以攻击者用rainbow table破解就不可行了,而且有exp定义超时时间,安全性有进一步提升。 实在不小心,SECRET泄漏了,攻击者可以实施的威胁就大的多了,不过这也是其它依赖SECRET的方案的一个通用问题(比如滴滴目前的方案)

     

    服务器端换SECRET,造成的影响不过是最近几分钟内用户获取的短信验证码无效,需要用户再获取一次而已,可以接受

  • 相关阅读:
    Java学习二十九天
    Java学习二十八天
    47. Permutations II 全排列可重复版本
    46. Permutations 全排列,无重复
    subset ii 子集 有重复元素
    339. Nested List Weight Sum 339.嵌套列表权重总和
    251. Flatten 2D Vector 平铺二维矩阵
    217. Contains Duplicate数组重复元素
    209. Minimum Size Subarray Sum 结果大于等于目标的最小长度数组
    438. Find All Anagrams in a String 查找字符串中的所有Anagrams
  • 原文地址:https://www.cnblogs.com/qq1069284034/p/8867419.html
Copyright © 2011-2022 走看看