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,造成的影响不过是最近几分钟内用户获取的短信验证码无效,需要用户再获取一次而已,可以接受

  • 相关阅读:
    解决Navicat导出Excel数字为科学计数法问题
    maven pom.xml文件中properties标签介绍
    Maven中配置maven-compiler-plugin 插件
    使用Spring Boot 优雅地发送邮件
    Intellij IDEA 设置JDK版本
    MySQL order by if()或order by in()条件排序
    Eclipse工具的简单使用
    Java中的四种权限修饰符及六种非访问修饰符(简识)
    原来你的电脑就是这样被木马远控了!!!(转载)
    记Java中有关内存的简单认识
  • 原文地址:https://www.cnblogs.com/qq1069284034/p/8867419.html
Copyright © 2011-2022 走看看