ThinkPHP5在API方面请求数据的安全处理

假设我们在做TP5项目的时候 需要编写api接口提供给外部进行请求,过程中肯定会涉及到安全问题:

主要是:

  1. 接口请求地址和参数暴露
  2. 重要接口返回数据明文暴露
  3. APP登陆态请求的数据完全性问题
  4. 代码层的数据完全问题

利用以上的问题 想攻击你的人 可以不断去请求你的后台 或者往你的后台塞东西进去 又或者是通过抓包的形式 拿到你的数据值之后不断的攻击 因此 考虑到时效性 唯一性  需要实现一个sign字段 在规定的时间内失效 没有这个字段无法请求 并且是唯一的


以下代码实现:

  1. 关于sign的校验(采取加密的方式)
  2. sign的唯一性验证
  3. sign的有效时间和缓存时间

加密方式:MD5 AES RSA  我们采用AES加密,基本参数都放入header(sign version app_type did model) 

每次httpq请求都携带sign(放入header)

首先引入一个Aes文件到目录:application\common\lib\Aes.php(文件夹不存在就新建)

简单介绍Aes文件的代码:

构造函数中的key 需要进行配置 

encrypt() 加密的方法  

decrypt() 解密的方法

 

key = config('app.aeskey');
    }

    /**
     * 加密  加密模式也要一致 pkcs5
     * @param String input 加密的字符串
     * @param String key   解密的key
     * @return HexString
     */
    public function encrypt($input = '') {
        $size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
        $input = $this->pkcs5_pad($input, $size);
        $td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
        $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
        mcrypt_generic_init($td, $this->key, $iv);

        $data = mcrypt_generic($td, $input);
        mcrypt_generic_deinit($td);
        mcrypt_module_close($td);
        $data = base64_encode($data);

        return $data;

    }
    /**
     * 填充方式 pkcs5
     * @param String text 		 原始字符串
     * @param String blocksize   加密长度
     * @return String
     */
    private function pkcs5_pad($text, $blocksize) {
        $pad = $blocksize - (strlen($text) % $blocksize);
        return $text . str_repeat(chr($pad), $pad);
    }

    /**
     * 解密
     * @param String input 解密的字符串
     * @param String key   解密的key
     * @return String
     */
    public function decrypt($sStr) {
        $decrypted= mcrypt_decrypt(MCRYPT_RIJNDAEL_128,$this->key,base64_decode($sStr), MCRYPT_MODE_ECB);
        $dec_s = strlen($decrypted);
        $padding = ord($decrypted[$dec_s-1]);
        $decrypted = substr($decrypted, 0, -$padding);

        return $decrypted;
    }

}

key的配置以及一些sign的配置 代码贴上 :

位置:application\extra\app.php

 'zzs',//密码加密
    'aeskey'=>'zzs45747ss223455',//aes加密的密钥  客户端和服务端一致
    //app_type配置数组
    'app_types'=>[
        'ios','android'
    ],
    'app_sign_time'=>10,//sign失效时间 秒  测试的时候 设置长一些
    'app_sign_cache_time'=>20,//sign缓存的有效时间
];

现在编写common基类 所有的api接口去继承它! 代码的讲解都写在注释中

checkRequestAuth();
        $this->testAes();
    }
    //验证数据是否合法 检查每次请求
    public  function  checkRequestAuth(){
        //首先获取header
        $headers = request()->header();

        //进行参数校验
        if(empty($headers['sign'])){
            throw new ApiException('sign参数不存在!',400);
        }

        //判断app_type是否存在配置的数组中需要在app.php下进行配置application\extra\app.php
        if(!in_array($headers['app_type'],config('app.app_types'))){
            throw new ApiException('app_type不合法!',400);
        }
        //如果sign校验不能通过
       if(!IAuth::checkSignPass($headers)){
            throw new ApiException('授权码sign失败!',401);
       }
       //sign做唯一性处理  写入缓存
        Cache::set($headers['sign'],1,config('app.app_sign_cache_time'));
       //赋值给类的属性
       $this->headers = $headers;

    }
    //测试aes
    //sign  AES加密  创建Aes类库 application\common\lib\Aes.php
    //sign加密方法  封装在application\common\lib\IAuth.php
    public function  testAes(){
//        $str = "id=1&ms=45&username=zzs";
//        //调用aes类库下的加密方法
//        $a = new Aes();
//        //这是加密的方法
//        $a->encrypt($str);
//        //解密的方法
//        $a->decrypt($res);
//        exit;

        //测试封装的sign加密方法
        $data = [
            'did'=>'18250305186',
            'version'=>1,
            'time'=>Time::get13TimeStamp(),//时间的类库

        ];
            //加密结果  6c6gWTBo42bqv5XNyMYvy2ejqHAfeu+GWCHCJHAo7PA= 没有带时间的串
        //6c6gWTBo42bqv5XNyMYvy+92AigFr1Pgmqa2XeKzQoVMQ1ADQBQaQ2mrP5zmM/mu 带时间的串
        //echo IAuth::setSign($data);exit;

//        //反转换
        //$str = "6c6gWTBo42bqv5XNyMYvy2ejqHAfeu+GWCHCJHAo7PA=";//
        //解密  did=12345dg&version=1
       // echo (new Aes())->decrypt($str);exit;

    }
}

以上代码中的 IAuth::checkSignPass 这边说明一下 就是创建一个类封装了sign加密方法和 检验sign是否能通过的方法,代码如下:位置:application\common\lib\IAuth.php

encrypt($string);
        //返回结果
        return $string;
    }

    //检验sign是否能通过

    /**
     * @param array  $data $headers的数据
     * @return boolean
     */
    public static  function  checkSignPass($data){
        //解密
        $str = (new Aes())->decrypt($data['sign']);
        //如果解密之后为空
        if(empty($str)){
            return false;
        }
        //将$str解析成多个变量
        parse_str($str,$arr);
//        array(2) {
//            ["did"] => string(7) "12345dg"
//            ["version"] => string(1) "1"
//        }
        //halt($arr);//打印出数据
        //如果不是一个数组 或者 did的值为空 或者 did的值不等于提交过来的值
        if(!is_array($arr) || empty($arr['did']) || $arr['did'] != $data['did']){
            return false;
        }
        //判断是否在时间范围内 10分钟 超过时间sign的内容就错误了
        if(time()-ceil($arr['time'] / 1000) > config('app.app_sign_time')){
            return false;
        }
        //唯一性判定 如果存在 返回false
        if(Cache::get($data['sign'])){
            return false;
        }
        return true;
    }
}

接下来开始测试,新建一个api接口Test.php(application\api\controller\Test.php)创建一个update方法 传参id

encrypt(input('put.')),201);

    }
    
    
    
}

然后打开postman 选择请求方法是 put 然后api的地址  比如我的是localhost/test/1(localhost就是本地的ip)

选择headers标签  如下图:

ThinkPHP5在API方面请求数据的安全处理_第1张图片

 

然后点击send 接下来我们分析上面的那些代码 讲解以下实现过程:

1.这个test类是继承common类

所以走到这里 初始化方法里面 调用我们验证数据的方法

ThinkPHP5在API方面请求数据的安全处理_第2张图片

 2.看下我们这个验证数据的方法checkRequestAuth()

ThinkPHP5在API方面请求数据的安全处理_第3张图片

首先先获取header的信息 这是一个数据

然后判断这个数组中有没有提交sign这个字段 如果没有 断开 提示报错

接下来再判断 提交的app类型是否在config配置中 如果不是 同样 断开 提示报错

然后调用IAuth类下面的方法 进行sign验证  看下代码:

ThinkPHP5在API方面请求数据的安全处理_第4张图片

通过这些判断 才能返回真  否则回到Common的文件中下的判断 抛出错误授权码sign失败!

以上的判断都能通过之后 写入缓存中  设置标识 才具有唯一性 并配置他的存储时间长度

ThinkPHP5在API方面请求数据的安全处理_第5张图片

然后赋值给header 这样在基类中进行判断无误之后 我们在Test.php下的update方法进行打印

在此之前 记得在postman的body标签 提交一些数据 比如我的:

ThinkPHP5在API方面请求数据的安全处理_第6张图片

在Test.php下的update方法下 我们进行打印:(此时app_sign_time的时间设置的长一些)

编写好了之后  回到postman  点击send

ThinkPHP5在API方面请求数据的安全处理_第7张图片

说明测试成功  data就是我们提交之后 调用方法 进行加密的结果 

 

思路表达可能有些紊乱 欢迎在评论区留言 觉得有用 就点个赞 加关注吧

你可能感兴趣的:(PHP,TP5接口请求安全)