前言:

在写开放到外部的API接口时是如何保证数据的安全性的?

在外部用户访问开放的api接口中,我们通过http Post或者Get方式请求服务器的时候,

会遇到以下问题:

请求身份是否合法

请求参数是否被篡改

请求的唯一性


为了保证数据在通信时的安全性,我们可以采用参数签名的方式来进行相关验证。


一、示例:

如:客户端client需要调用平台开放api接口进行数据查询


流程其实很简单,不难理解,

client查询==>调用api接口进行查询==>返回查询结果==>显示在client


按照普通未做安全验证的方式就是这样调用:

client调用:

http://ip:port/interfaceName?参数1=value1.......

如上,这种方式简单但是安全问题很大,

通过调用以上链接方式是可以获取产品列表信息了,但是这样的方式会存在很严重的安全性问题,没有进行任何的验证,大家都可以通过这个方法获取到产品列表,导致产品信息泄露;

同时还容易被恶意盗取信息,比如说可以参数1的值改成1,2,3,4...不断的去类似“撞库”去调用。



二、使用数据签名(使用sha或者Md5,获取其它算法)

针对以上问题,我们对开放平台的api接口进行优化:


1.给每个客户端client分配对应的key、secret,可以理解成:用户名密码;

2.Sign签名:调用API时需要对请求参数进行签名验证,签名方式如下:


a. 按照请求参数名称将所有请求参数按照字母先后顺序排序(如果参数层次比较多可以只做一级排序),

$param = array(
	'access_token' => '',
	'app_key' => '152968d9af768bf084dad750f78d6866',
	'client' => '{"channel":"mcontact_md_ahlrj_api_android","imei":"1","version":"v3.9.8"}',
	'once' => '911091697599',
	'phone' => '13800138000',
	'sdk_from' => 'java',
	'type' => 0,
	'version' => '1.0',
);


b.然后将参数名和参数值进行拼接得到参数字符串,如:

access_token=app_key=152968d9af768bf084dad750f78d6866client={"channel":"mcontact_md_ahlrj_api_android","imei":"1","version":"v3.9.8"}once=911091697599……


拼接完后,在最后面再拼接上分配的secret然后用sha对拼接串加密得到签名验证字符串sign,

如:

平台api接口代码:

sign=BCC7C71CF93F9CDBDB88671B701D8A35
public Response interfaceName(HttpRequest request){
	//用户验证,判断key是否存在,并根据key查询出secret用于验证签名
	//....
	//验证sign签名,根据前面说的排序等算法把参数进行签名一次得到新的sign和参数中的sign对比
	//...
	//查询数据做处理
	//...
	return //返回处理结果
}


开放api接口签名验证

3.调用

client调用:

http://ip:port/interfaceName?app_key=app_key&sign=BCC7C71CF93F9CDBDB88671B701D8A35&参数1=value1&参数2=value2.......

即:将参数和sign签名一起传到api接口

注:secret 仅作加密使用, 为了保证数据安全请不要在请求参数中使用。


如上,优化后的请求多了key和sign参数,这样请求的时候就需要合法的key和正确签名sign才可以获取产品数据。这样就解决了身份验证和防止参数篡改问题,如果请求参数被人拿走,也拿不到secret,因为secret是不传递的。再也无法伪造合法的请求。


如此便完成了签名接口api调用。


备注:

以上方式还不是不够完美的,还是会有小问题如果获取了上面的完整链接,一直使key和sign和一样的参数还是可以正常获取数据的。

因此需要保证“请求的唯一性”:


为了防止别人重复使用请求参数问题,我们需要保证请求的唯一性,就是对应请求只能使用一次,这样就算别人拿走了请求的完整链接也是无效的。唯一性的实现:在如上的请求参数中,我们加入时间戳 :timestamp(yyyyMMddHHmmss),同样,时间戳作为请求参数之一,也加入sign算法中进行加密。

平台api接口代码:

public Response interfaceName(HttpRequest request){
	//用户验证,判断key是否存在,并根据key查询出secret用于验证签名
	//....
	//验证sign签名,根据前面说的排序等算法把参数进行签名一次得到新的sign和参数中的sign对比
	//...
	//验证有效期
	//...
	//查询数据做处理
	//...
	return //返回处理结果
}


开放api接口签名验证

client调用:

http://ip:port/interfaceName?app_key=app_key&sign=BCC7C71CF93F9CDBDB88671B701D8A35×timestamp=201603261407&参数1=value1&参数2=value2.......

如上,我们通过timestamp时间戳用来验证请求是否过期。这样就算被人拿走完整的请求链接也是无效的。


关于时间戳验证可以设置单次或者一段时间有效期。