今年五月份去融贝网、猎豹移动面试的时候,被问到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接口加密策略