接口请求参数防篡改检验方案
目的
接口请求的参数如果没做防篡改的参数校验,攻击者可以拦截接口并修改参数,这样后端是不知道参数从前端发起后是否被篡改。比如小游戏游戏部分在前端,完成游戏后提交成绩给后端,攻击者可以抓包请求进行拦截篡改游戏成绩,这点已经被证明是可行的。基于此,打算指定一套前端与后端约定的参加校验规则,提高篡改数据的难度。
真正防范篡改数据很困难,因为加密规则都在前端代码里,有一定技能的人还是可以通过看前端代码获得加密校验算法,所以本文只是提高篡改参数的难度。之前想用对称加密,用一个密钥加密,但密钥在前端代码里,还是能获取到。后来又想动态获取密钥,但这样实现起来复杂很多。所以本文只是简单地做一些参数校验,把大部分恶意者拦截了。
加密思路
前端/客户端请求参数时,增加时间戳字段、校验码字段,也就是需要3个字段,分别是data、timestamp、sign,其中data字段为业务数据参数,timestamp为当前的时间戳,sign为参数校验字段。sign字段由 data+timestamp 进行md5。
###接口请求参数
data: 业务数据,采用json格式,可以为空。
timestamp:时间戳,即从格林威治时间1970年01月01日00时00分00秒起至现在的总秒数。
sign:参数校验字段,规则 MD5(data+timestamp),也可以再加一些盐值,不同项目可以做不同的约定,这里就不写了。
具体加密过程
这里附上后端代码,前端可以参考。
-
业务数据data用json格式,假设为:
$data = {"text":"我最棒"}
-
时间戳字段timestamp,假设现在时间是2018-10-01 08:00:00,转换成时间戳即为:
$timestamp = 1538352000
-
对上面两个参数进行md5:
$sign = md5($data.$timestamp); //即 md5('{"text":"我最棒"}1538352000'); /** * 得到md5 加密内容 * $sign = '920f2aa820cbd374efa63ff5b5c465f4'; **/
参数校验解密过程
时间校验
客户端已经传了timestamp
字段,但客户端的时间可能跟服务器的时间不一样,加上传输过程有延时,所以不能简单地和服务器的时间戳相对进行对比,需要允许一点时间差。
/**
* 参数防篡改校验
*/
private function signValidate()
{
$parameters = app()->request->input();
$data = $parameters['data'] ?? '';
$timestamp = $parameters['timestamp'] ?? '';
$sign = $parameters['sign'] ?? '';
if (empty($timestamp) || empty($sign)) {
throw new ApiInternalServerException('缺少校验码', RetCode::ERR_WRONG_SIGN);
}
//校验时间差
if (abs($timestamp - time()) > 30) {
throw new ApiInternalServerException('该请求时间差有错', RetCode::ERR_WRONG_SIGN);
}
//检验具体参数
if ($sign != md5($data . $timestamp)) {
throw new ApiInternalServerException('检验码有误', RetCode::ERR_WRONG_SIGN);
}
}
取消校验
测试调试的时候可以关闭校验,在.env文件设置是否校验的开关。
Postman 设置
在开发的时候需要实时获取时间戳并进行MD5,若人工每次都去计算一下,那实在太蠢了,利用postman的发送请求前可以执行脚本和设置环境变量的功能,我们可以轻松地解决这个问题。在 Pre-request Script
tab页增加以下代码设置环境变量:
var timestamp = Math.floor((new Date().getTime())/1000);
var requestData = request.data["data"] || '';
var sign = CryptoJS.MD5(requestData + timestamp);
postman.setEnvironmentVariable("timestamp",timestamp);
postman.setEnvironmentVariable("sign",sign);
postman.setEnvironmentVariable("signtest",requestData + timestamp);
在请求的Body
页添加以下参数:
KEY | VALUE |
---|---|
timestamp | {{timestamp}} |
sign | {{sign}} |
前端封装好请求接口后,也不需要管这两个参数了。对前端开发的调试没造成多大的困扰。但每个请求接口在postman那都要设置一下这个Pre-request Script
,有点麻烦,不知各位有没好的解决方案。