一次性搞懂服务端API安全解决方案

今年五月份去融贝网、猎豹移动面试的时候,被问到API安全加密的问题,很惭愧,自己回答的很不全面。自己也知道那是没有彻底弄明白原理。然后,8月份的时候,上家公司新项目启动时也和同事探讨过,然后就去彻彻底底了解了一下,趁着热乎劲儿还没过去总结出来吧。

安全是相对的,下面是根据安全级别分析。我用简单的PHP代码演示出来,只是演示,代码写的不严谨请轻喷啊!!!

1、完全开放的接口

这种我们称之为“裸奔API”,只要连上网就能调用,不存在安全,一般只能查询,不能执行增、删、改的操作。

<?php

// 接口对外开放
public function noSecure($string)
{
	$data = DB('table')->where('string', $string)->get();
	if(!is_null($data)) {
		return $data;
	}
}

复制代码

2、接口参数加密(基础加密)

这种加密方式,只想让特定的调用方使用。好比你把这些调用的人叫到一个小屋子,给他们宣布说我这里有个接口只打算给你们用,我给你们每人一把钥匙,你们用的时候拿着这把钥匙即可。

这把钥匙就是我上文说到的参数加密规则,有了这个规则就能调用。

这有安全问题啊,这里面的某个成员如果哪个不小心丢了钥匙或者被人窃取,掌握钥匙的人是不是也可以来掉用接口了呢?而且他可以复制很多钥匙给不明不白的人用。

相当于有人拿到了你的请求链接,如果业务没有对链接唯一性做判断(实际上业务逻辑通常不会把每次请求的加密签名记录下来,所以不会做唯一性判断),就会被重复调用,有一定安全漏洞,怎么破?先看这个场景的代码,然后继续往下看!

<?php

// 接口加密
public function secureBySign($string, $appKey, $sign)
{
	//检验签名是否合法
	$string = $_POST("string");
	$appKey = $_POST("appKey");
	$sign = $_POST("sign");
	
	$signHelper = new SignHelper();
	$currentSign = $signHelper->getSign($appKey, $string, ……);

	if($sign !== $currentSign) {
		return "签名不合法";
	}
	
	$data = DB('table')->where('string', $string)->get();
	if(!is_null($data)) {
		return $data;
	}
}

复制代码

3、接口参数加密+接口时效性验证(一般达到这个级别已经非常安全了)

继上一步,你发现有不明不白的人调用你的接口,你很不爽,随即把真正需要调用接口的人又叫来,告诉他们每天给他们换一把钥匙。和往常一样,有个别伙伴的钥匙被小偷偷走了,小偷煞费苦心,经过数天的踩点观察,准备在一个月黑风高的夜晚动手。拿出钥匙,捣鼓了半天也无法开启你的神圣之门,因为小偷不知道你天天都在换新钥匙。

小偷不服,经过一段时间琢磨,小偷发现了你们换钥匙的规律。在一次获得钥匙之后,不加思索,当天就动手了,因为他知道他手里的钥匙在第二天你更换钥匙后就失效了。

结果,小偷如愿。怎么破?先看这个场景的代码,然后继续往下看!

<?php

// 接口加密并根据时间戳判断有效性
public function secureBySignExpired($string, $appKey, $sign, $timestamp)
{
	//判断请求是否过期---假设过期时间是20秒
	$requestTime = GetDateTimeByTicks($timestamp);
	if(($requestTime + 20) < $_SERVER["REQUEST_TIME"]) {
		return "接口过期";
	}
	
	//检验签名是否合法
	$string = $_POST("string");
	$appKey = $_POST("appKey");
	$sign = $_POST("sign");
	
	$signHelper = new SignHelper();
	$currentSign = $signHelper->getSign($appKey, $string, ……);

	if($sign !== $currentSign) {
		return "签名不合法";
	}
	
	$data = DB('table')->where('string', $string)->get();
	if(!is_null($data)) {
		return $data;
	}
}

复制代码

4、接口参数加密+时效性验证+私钥(达到这个级别安全性固若金汤)

继上一步,你发现道高一尺魔高一丈,仍然有偷盗事情发生。咋办呢?你打算下血本,给每个人配一把钥匙的基础上,再给每个人发个暗号,即使钥匙被小偷弄去了,小偷没有暗号,任然无法如愿。即使小偷真正的如愿,这样也很容易定位是谁的暗号泄漏问题,找到问题根源,只需要给当前这个人换下钥匙就行了,不用大动干戈。

但这个并不是万无一失的,因为钥匙和暗号毕竟还有可能被小偷搞到。代码如下:

<?php

// 接口加密并根据时间戳判断有效性而且带着私有key校验
// 在调用接口时动态从库里取,每个调用方在调用时带上他的appSecret,调用方一般把自己的appSecret放到网站或APP配置文件中
public function secureBySignExpiredKeySecret($string, $appKey, $sign, $timestamp)
{
	//判断请求是否过期---假设过期时间是20秒
	$requestTime = GetDateTimeByTicks($timestamp);
	if(($requestTime + 20) < $_SERVER["REQUEST_TIME"]) {
		return "接口过期";
	}

	// 根据appkey查库获取appSecret值
	$appSecret = DB('table')->where('appKey', $appKey)->get('appSecret');
	
	//检验签名是否合法
	$string = $_POST("string");
	$appKey = $_POST("appKey");
	$sign = $_POST("sign");
	
	// 把appSecret加入进行加密
	$signHelper = new SignHelper();
	$currentSign = $signHelper->getSign($appKey, $appSecret, $string, ……);

	if($sign !== $currentSign) {
		return "签名不合法";
	}
	
	$data = DB('table')->where('string', $string)->get();
	if(!is_null($data)) {
		return $data;
	}
}

复制代码

5、接口参数加密+时效性验证+私钥+Https(我把这个级别称之为金钟罩,世间最安全莫过于此)

安全第五层,在第四层的基础上加HTTPS,关于HTTPS怎么加,可以自行去了解,网上相关资料一堆。

关于加密算法可以参考:Api接口加密策略

猜你喜欢

转载自juejin.im/post/5bf10deb6fb9a049ae077b5b