背景
api的常用就不用多说了,在这个网络时代,小到天气信息,大到各种大数据平台,我们的生活中充斥着各种各样的api,面对各种复杂的用户使用场景,你永远不知道请求你的api的是一个普普通通的小白用户,还是一个分分钟偷的你连内裤品牌都藏不住的黑客,所以api的安全就非常重要了。
除了日常的漏洞修复、服务器维护,api的安全验证防御也是特别重要的一环。
api接口安全类型
api接口安全类型一般有以下几种类型(不完全,能想到的就这些了,欢迎交流补充):
防御类型 | 防御方法 |
---|---|
1.参数篡改 | url签名方式 |
2.防御未授权用户访问 | 用户token验证 |
3.防御未授权应用或者爬虫 | appid,appsecret保证授权访问 |
4.防御dos公积浪费资源 | 时间戳timestamp |
5.防御重要信息泄露 | https加密传输 |
6.防御重放攻击 | 时间戳、随机数等方法 |
献丑代码(TP5.2):
protected $notCheckUserToken = array('V1.User/login', 'V1.User/register', 'V1.User/sendcode');// 不需要检测用户登录的请求
/**
* 公共初始化验证信息
*/
protected function initialize()
{
parent::initialize();
$this->checkTime($this->request->only(['time'])); // 验证时间是否超时
$this->param = $this->request->param(true);// 获取参数
$this->checkParam($this->param); // 验证参数规则,合法有效性
$this->checkSign($this->param); // 验证参数签名,方式参数篡改
// 排除不需要验证登录的请求后检查用户token
if (!in_array($this->request->controller().'/'.$this->request->action(), $this->notCheckUserToken)) {
$token = isset($this->param['token']) ? $this->param['token'] : '';
$this->param['uid'] = $this->checkUserToken($token);
}
}
1.参数篡改
防止参数篡改就是用md5或者sha1等加密函数或者自定义加密方法对请求的URL在客户端进行加密生成sign/access_token,在请求时带上这个生成的sign/access_token,以备服务器验证。当请求到达服务器时,用同样的加密方式对url进行加密,对比加密结果和客户端请求带来的sign/access_token,若两者一致,则有效访问,反之返回错误。以此来达到防参数篡改的目的。
为了更严谨安全,通常也会增加“盐”参数,“盐”值可以预先保存在客户端,加密时作为参数参与加密,也可以通过动态的方式预先请求服务器获取动态“盐”值。
举一个例子(PHP):
/**
* 服务器端根据参数生成请求签名
* 对参数进行字典排序,然后组合成字符串,先进行sha1编码,然后对产生的结果进行MD5加密
* @param $arr 参数集合
* @return string
*/
public function generateSign($arr)
{
// $app_secret 是一个静态随机字符串,在没有和appid绑定的情况下,相当于一个“盐”nonce
unset($arr['sign']);
ksort($arr, SORT_STRING); // 对除sign以外的请求的参数进行字典排序
$str = $app_secret.implode('', $arr); // 将参数数组组合字符串
$serverSign = md5(sha1($str));
return $serverSign;
}
下面是验证签名的方法:
/**
* 参数验证签名
*
* @param $arr
*
*/
public function checkSign($arr)
{
if (!isset($arr['sign'])) {
$this->returnMsg(401, '请求签名缺失!');
}
$clientSign = $arr['sign'];
if ($clientSign != $this->generateSign($arr)) {
$this->returnMsg(401, '请求签名错误!');
}
}
ruturnMsg方法内容:
/**
* 返回数据统一处理json格式返回
*
* @param $code 状态码
* @param string $msg 消息字符串
* @param array $data 返回数据
*/
public function returnMsg($code, $msg = '', $data = [])
{
echo json_encode(['code' => $code, 'msg' => $msg, 'data' => $data]);
die;
}
2.未授权用户访问
对于一些需要用户信息的请求,需要判断用户是否是授权用户(注册用户/或者特殊权限),这时候需要使用user_token(token)
session保存),并返回给客户端token保存。当用户在下一次请求需要用户验证的url资源时,带上token值,请求到达服务器后,与服务器端保存的token进行比对,一直则有权限访问,反之则决绝客户端请求并返回标识,客户端跳转到登录页。
生成token:
/**
* 生成userToken用户token信息
* 生成方式:md5($uid+毫秒时间戳+4位随机数)
*
* @param $uid
* @return string
*/
protected function generateToken($uid)
{
return md5($uid.uniqid().rand(1111, 9999));
}
验证token:
/**
* 检查用户token,判断登录状态,还是要改成数据库redis存储比较好
*
* @param $token
* @return mixed
*/
public function checkUserToken($token)
{
if (!$token) $this->returnMsg(401, 'token参数缺失!');
if (!Session::has($token)) {
$this->returnMsg(401, '请先登录!');
}
return session($token);
}
3.dos攻击
先介绍一下dos攻击:
DoS攻击是指故意的攻击网络协议实现的缺陷或直接通过野蛮手段残忍地耗尽被攻击对象的资源,目的是让目标计算机或网络无法提供正常的服务或资源访问,使目标系统服务系统停止响应甚至崩溃,而在此攻击中并不包括侵入目标服务器或目标网络设备。这些服务资源包括网络带宽,文件系统空间容量,开放的进程或者允许的连接。这种攻击会导致资源的匮乏,无论计算机的处理速度多快、内存容量多大、网络带宽的速度多快都无法避免这种攻击带来的后果。
为了防御dos攻击,在请求中加入时间戳timestamp是一个非常有效的办法,让地址具有超时机制,在设定的时间范围内(5-10分钟)可以访问,反之则拒绝请求,时间戳超时机制是防御dos攻击的有效手段。
/**
* 验证时间戳,客户端和服务器请求时间差不能大于10分钟
*
* @param $arr 参数数组
*/
public function checkTime($arr)
{
if (!isset($arr['time']) || intval($arr['time']) <= 1) {
$this->returnMsg(401, '时间戳不正确!');
}
if (time()-$arr['time'] > 600) {
$this->returnMsg(401, '请求超时!');
}
}
4.重要信息泄露
重要信息泄露,对于一些安全性较高的应用或者银行卡密码等机密信息来说,传递明文信息是非常不安全的,所以就诞生了https安全访问,SSL加密由Netscape公司在1994年发明,使用SSL+http的方式,实现http的加密访问,后来出现了TLS加密,是在SSL 3.0的基础上演化而来,但是两者加密算法不同,所以两者之间不通用。
应用或者web要使用https,需要证书。
5.重放攻击
先解释一下重放攻击:
重放攻击(Replay Attacks)又称重播攻击、回放攻击或新鲜性攻击(Freshness Attacks),是指攻击者发送一个目的主机已接收过的包,来达到欺骗系统的目的,主要用于身份认证过程,破坏认证的正确性。
它是一种攻击类型,这种攻击会不断恶意或欺诈性地重复一个有效的数据传输,重放攻击可以由发起者,也可以由拦截并重发该数据的敌方进行。攻击者利用网络监听或者其他方式盗取认证凭据,之后再把它重新发给认证服务器。从这个解释上理解,加密可以有效防止会话劫持,但是却防止不了重放攻击。重放攻击任何网络通讯过程中都可能发生。重放攻击是计算机世界黑客常用的攻击方式之一,它的书面定义对不了解密码学的人来说比较抽象。
重放攻击的基本原理就是把以前窃听到的数据原封不动地重新发送给接收方。很多时候,网络上传输的数据是加密过的,此时窃听者无法得到数据的准确意义。但如果他知道这些数据的作用,就可以在不知道数据内容的情况下通过再次发送这些数据达到愚弄接收端的目的。例如,有的系统会将鉴别信息进行简单加密后进行传输,这时攻击者虽然无法窃听密码,但他们却可以首先截取加密后的口令然后将其重放,从而利用这种方式进行有效的攻击。再比如,假设网上存款系统中,一条消息表示用户支取了一笔存款,攻击者完全可以多次发送这条消息而偷窃存款。
对于重放攻击,上面的解释中有一个词最重要,就是重复发送,解决方案就是打破重复,防御方法一般有三种(来源百度):
- 加随机数。该方法优点是认证双方不需要时间同步,双方记住使用过的随机数,如发现报文中有以前使用过的随机数,就认为是重放攻击。缺点是需要额外保存使用过的随机数,若记录的时间段较长,则保存和查询的开销较大。
- 加时间戳。该方法优点是不用额外保存其他信息。缺点是认证双方需要准确的时间同步,同步越好,受攻击的可能性就越小。但当系统很庞大,跨越的区域较广时,要做到精确的时间同步并不是很容易。
- 加流水号。就是双方在报文中添加一个逐步递增的整数,只要接收到一个不连续的流水号报文(太大或太小),就认定有重放威胁。该方法优点是不需要时间同步,保存的信息量比随机数方式小。缺点是一旦攻击者对报文解密成功,就可以获得流水号,从而每次将流水号递增欺骗认证端。
在实际中,常将方法(1)和方法(2)组合使用,这样就只需保存某个很短时间段内的所有随机数,而且时间戳的同步也不需要太精确。
对付重放攻击除了使用以上方法外,还可以使用挑战一应答机制和一次性口令机制,而且似乎后面两种方法在实际中使用得更广泛。
总结
以上零零总总的写了几种api安全验证的方法,防御各种各样的攻击和恶意请求。当然一般来讲,普通的api会选择以上方法中的几种或者自己定义更复杂的验证,还是需要根据自己的需要来选择使用,毕竟最适合的才是最好的。