移动端后台开发 (加密验证--通用接口篇)
前言
在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;
}
代码与注解:
-
时间验证
public function checkTime($time) { $Time_difference = abs(time()-$time); if($Time_difference>30){ return false; } return true; }
请求到达服务器的时间减去移动端本地的时间戳, 如果大于
30
秒, 就代表请求超时,有可能是该用户的网络不算太好。如果不使用abs()
函数的话,万一我的数据包被别人抓住了他可以无限大的修改$time
这个值,那么使用服务器时间戳去相减的到的一直是负数,还是小于30
秒的。由此可见使用abs()
函数会更加的安全一些。 -
验证
ID
的存在
主要是为接下来的非通用接口验证做准备。通用接口的
ID
可以与移动端事前定义一个值。"移动端与服务端ID的值一定要相同,要不然没办法做接下来的验证"
- 根据移动端传递的版本号来进行加密验证
每个大版本的改变,加密的方式都应随着改变而改变。小版本的修正就没有必要去更换加密方式了。
-
通用加密验证
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 协议》,转载必须注明作者和本文链接
abs
没有必要,假如攻击者能够修改$time,他就一直使用当前时间戳就可以了。@leo
1.使用当前时间戳的话,抓包改包是需要时间。
2.任何的加密方式都不是防君子,防的只是技术不算高,但又想给你瞎怼两下的人
@JRzhang 因为接口有签名机制,如果攻击者只是简单修改$time参数,那根本过不了验证,这个情况你不判断$time是否超时都没关系,因为进不到后面逻辑。如果攻击者已经破解出加密逻辑和签名key,那他在伪造请求的时候把$time设成当前时间,过了签名校验也过了时间校验,所以这里的
abs
并没有任何价值。@leo 受教了
写前言这些不要用写代码的格式写,真的要标注就用下面那种 > xxx
其他也是,看着好累:joy:
abs
还是有必要的 没被破之前
@JokerLinly 感觉好多了
。。版本比较你见过这个函数吗
version_compare()
这两个定义好奇怪啊,
像"个人信息","支付密码",这种涉及到个人隐私或者金钱的接口,我们称为"非通用接口"
这个不是用户认证事情吗,jwt?oath?,你能证明你是你了,我就让你访问你自己的信息。另外支付密码还能通过接口获得?
抛开用户认证,就是所有接口不能随便调用,对请求参数做个签名,过滤一下没有反编译拿到签名算法的人。
@杨进春 如果有很多版本就需要用到很多
if
why NOT jwt ?
移动端用户在登录时,服务端给个 token ,以后所有的请求,都要求带上token (在header里)。只要解析这个 token ,就能知道是谁了,想要仿冒得弄到原主的这个 token 。
弄到了原主的 token,还得知道原主的guid和其他信息,因为服务端也会比对。
还有更神经的,单次 token ,每次请求带 token ,返回一个新 token ,下次不是这个 token 就不行了。为了防止短时间多次请求,还有token缓冲期,多了去了。
@qufo 因为我不会啊
@qufo 目前也是在使用
JWT
来前后端通信,方便极了。@JRzhang
JWT
使用很方便并且已经算是成熟解决方案,建议了解下。@杨进春 有了passport为什么还要jwt?
@leo 是的,就在刚刚我破解了一个 APP的签名规则,反编译 android 端的代码,里面的加密 key 和规则一目了然,用了 一天找出关键所在, 哈哈哈哈哈!!!!