移动端后台开发 (加密验证--通用接口篇)

前言

在Web开发中浏览器与服务器进行通讯,在服务器端我们可以用"session"来确定用户的身份,然后根据从浏览器接收到的参数,服务器返回给浏览器相应的数据。但是在移动端与服务器进行通讯时,并没有"session"的存在(也许是我不知道),不过我们可以要求移动端提交参数的时候把用户的"id"或"Guid"和其他参数一起提交过来。然后根据请求的参数来返回对应的内容.

那么问题来了,我可以使用抓包工具获取别人提交"url地址"与"参数",然后进行模拟操作,我就可以获取该用户的全部数据,如果涉及到金钱的话,更是一场毁灭性的灾难。所以,在开始一个移动端的项目前,加密验证是最先考虑与准备的事情.

毕竟谁也不希望,有人可以在自己家里面随意的进进出出!

一、接口分类

在我们日常接触的app中,像"登录","注册","找回密码"这种没有登录的情况下,都可以访问的接口,我们称为"通用接口",像"个人信息","支付密码",这种涉及到个人隐私或者金钱的接口,我们称为"非通用接口",再次重复一下,接口分为

1.通用接口 // 加密程度略低于非通用接口,但必须要做加密
2.非通用接口 // 必须是你知道的最安全的加密方式

附上代码

private static $verify;

// Verify 为注入文件
public function __construct(Verify $verify)
{
    self::$verify = $verify;
}

 private function verify($request)
{

    if($request->path()=='login'){
       // 通用方法
        return  self::$verify->common($request);
    }else{
       // 非通用方法 
        return self::$verify->proprietary($request);
    }
    return false;
}

public function handle($request, Closure $next)
{
    $time = time();
     // 此处调用上边的verify方法,根据返回的状态码,来返回移动端信息提示
    switch($this->verify($request))
    {
        case "SN200":
            $temp =  $next($request);
            return $temp;
            break;
        //最后在完善其他状态码信息 
    }

}

下面我们看看访问 login 路由时, 通用方法都做了什么?

 public function common($request)
{
   // 接收request对象
    if($request->all()){
        $data = $request->all();
        // 时间验证
        // $data['time']为移动端当前时间戳
        $ckTime = $this->checkTime($data['time']);
        // SN002 请求超时 
        if(!$ckTime) return 'SN002';
        // 验证ID的存在
        // SN004 用户没有登录
        if(!isset($data['id'])) return 'SN004';
            // 根据移动端传递的版本号来进行加密验证
            switch ($data['version']){
                case $data['version'] >'1.4' && $data['version']<'2.0':
                        // 开始进行通用加密验证
                    $temp = $this->checkCommon_v1($request);
                    break;
                default:
                    $temp = $this->checkCommon_v2($request);
                    break;
            }
            if($temp){
               // SN200 验证通过
                return "SN200";
            }
            // SN005 签名错误
            return "SN005";
    }
     // 服务器有请求,但参数为空,通常与安卓和ios的框架有关系(需注意)
    return false;
}

代码与注解:

  1. 时间验证

    public function checkTime($time)
    {
        $Time_difference = abs(time()-$time);
        if($Time_difference>30){
                return false;
        }
        return true;
     }

    请求到达服务器的时间减去移动端本地的时间戳, 如果大于 30 秒, 就代表请求超时,有可能是该用户的网络不算太好。如果不使用abs()函数的话,万一我的数据包被别人抓住了他可以无限大的修改$time这个值,那么使用服务器时间戳去相减的到的一直是负数,还是小于 30秒的。由此可见使用abs()函数会更加的安全一些。

  2. 验证ID的存在

主要是为接下来的非通用接口验证做准备。通用接口的ID可以与移动端事前定义一个值。"移动端与服务端ID的值一定要相同,要不然没办法做接下来的验证"

  1. 根据移动端传递的版本号来进行加密验证

每个大版本的改变,加密的方式都应随着改变而改变。小版本的修正就没有必要去更换加密方式了。

  1. 通用加密验证

    private function checkCommon_v1($request)
    {
            $data = $request->all();
            $path = $request->path();
            $time = $data['time'];
            $id = '1';
            $param = $data['param'];
            $cryptToken = "JRzhang";
            $signature = md5($path.$time.$id.$param.$cryptToken);
            if($signature!=$data['signatures']){
                    return false;
            }else{
                    return true;
            }
    }

这里说一下移动端应传递的参数

time 移动端当前时间
param 真正的请求参数 数组转为json后的字符串
version 移动端当前版本
deviceid 设备唯一识别码(非通用接口使用,用于单点登录)
signature 签名移动端应按照上边的参数顺序进行MD5加密
cryptToken 移动端保存一份与服务器端相同,但不进行参数传递只做加密使用

坑点: 服务器对 android 进行接口调试时,一定要注意 android本地的时间戳。因为android手机可以tmd调整本地时间.

最后如果移动端传递的signature与服务器端生成的$signature相同, 那么恭喜你,你可以从我家的大门进来了, 但只可以在院子里转一转, 如果想进屋里,请继续关注

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 6年前 自动加精
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 16
leo
  1. abs没有必要,假如攻击者能够修改$time,他就一直使用当前时间戳就可以了。
  2. 移动端加密api请求只能防君子不防小人,因为可以反编译出加密算法和key,只是时间问题。
7年前 评论

@leo
1.使用当前时间戳的话,抓包改包是需要时间。
2.任何的加密方式都不是防君子,防的只是技术不算高,但又想给你瞎怼两下的人

7年前 评论
leo

@JRzhang 因为接口有签名机制,如果攻击者只是简单修改$time参数,那根本过不了验证,这个情况你不判断$time是否超时都没关系,因为进不到后面逻辑。如果攻击者已经破解出加密逻辑和签名key,那他在伪造请求的时候把$time设成当前时间,过了签名校验也过了时间校验,所以这里的abs并没有任何价值。

7年前 评论

写前言这些不要用写代码的格式写,真的要标注就用下面那种 > xxx

xxxx

7年前 评论

其他也是,看着好累:joy:

7年前 评论

abs
还是有必要的 没被破之前

7年前 评论

。。版本比较你见过这个函数吗 version_compare()

7年前 评论
liyu001989

1.通用接口 // 加密程度略低于非通用接口,但必须要做加密

2.非通用接口 // 必须是你知道的最安全的加密方式

这两个定义好奇怪啊,像"个人信息","支付密码",这种涉及到个人隐私或者金钱的接口,我们称为"非通用接口" 这个不是用户认证事情吗,jwt?oath?,你能证明你是你了,我就让你访问你自己的信息。

另外支付密码还能通过接口获得?

抛开用户认证,就是所有接口不能随便调用,对请求参数做个签名,过滤一下没有反编译拿到签名算法的人。

7年前 评论

@杨进春 如果有很多版本就需要用到很多if

7年前 评论

why NOT jwt ?

移动端用户在登录时,服务端给个 token ,以后所有的请求,都要求带上token (在header里)。只要解析这个 token ,就能知道是谁了,想要仿冒得弄到原主的这个 token 。

弄到了原主的 token,还得知道原主的guid和其他信息,因为服务端也会比对。

还有更神经的,单次 token ,每次请求带 token ,返回一个新 token ,下次不是这个 token 就不行了。为了防止短时间多次请求,还有token缓冲期,多了去了。

7年前 评论

@qufo 因为我不会啊

7年前 评论

@qufo 目前也是在使用JWT来前后端通信,方便极了。
@JRzhang JWT使用很方便并且已经算是成熟解决方案,建议了解下。

7年前 评论

@杨进春 有了passport为什么还要jwt?

6年前 评论

@leo 是的,就在刚刚我破解了一个 APP的签名规则,反编译 android 端的代码,里面的加密 key 和规则一目了然,用了 一天找出关键所在, 哈哈哈哈哈!!!!

5年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!