接口设计安全
引言
我的理解:接口简单来说就是服务器端用来返回给其他程序或者客户端数据的桥梁。
安全要求
1. 防止伪装攻击(不知明方调用接口)
2. 防止请求篡改(请求的参数被篡改)
3. 防止重复攻击(请求被截获,被再次恶意调用)
3. 防止数据泄露(如登陆、支付信息)
签名验证
1. 服务端分配客户端特定的调用账号信息:
AppID:调用方身份ID,接口提供方用此来识别调不同的调用者
App_secret:用于生成签名sign
2. 接口调用方和接口提供方约定好统一的签名加密算法
3. 接口调用必要传入参数
参数名 | 类型 | 必选 | 描述 |
AppId | string | 是 | 调用方身份ID,接口提供方用此来识别调不同的调用者 |
paramet | mix | 否 | 请求参数 |
timestamp | int | 是 | 时间戳 |
sign | string | 是 | 一次接口调用的签名值,服务器端 “防止 伪装请求/防篡改/ 防重发” 识别的重要依据。 |
签名算法过程
1. 对除签名外的所有请求参数按key做的升序排列,value无需编码。
(假设当前时间的时间戳是12345678,appid为 testid)
例如:调用接口 www.example.com/v1/user/edit?c=3&b=2&a=1×tamp=12345678
有c=3,b=2,a=1 三个参,另加上时间戳后, 按key排序后为:a=1,b=2,c=3,timestamp=12345678。
2. 把参数名和参数值连接成字符串,得到拼装字符:a1b2c3timestamp12345678
3. 用申请到的appsecret 连接到接拼装字符串头部和尾部,然后进行32位MD5加密,最后将到得MD5加密摘要转化成大写。
示例:假设appkey=test256,strtouppermd5(test256a1b2c3timestamp12345678test256),取得MD5摘要值 C5F3EB5D7DC2748AED89E90AF00081E6 。
此时最终调用: www.example.com/v1/user/edit?c=3&b=2&a=1×tamp=12345678&appid=testid&sign=C5F3EB5D7DC2748AED89E90AF00081E6
服务端签名验证代码
1 # 签名的验证方法代码:
2 # $signtype 'yes':验证,'no':不验证
3
4 public function checkSign($args,$signature,$signtype = 'yes')
5 {
6 if($signtype == 'no') //上线时去除该部分,必须验证签名
7 {
8 return true;
9 }
10 if(!$args || !$signature)
11 {
12 return false;
13 }
14
15 //同一签名调用时间限制,可以设定 20s,50s,300s,避免多次重复调用
16 if (time() - $args['timestamp'] > 300)
17 {
18 return false;
19 }
20
21 //验证 appid 身份合法性,根据appid查询对应的 appsecret
22 $model=Model::where("appid=:appid")->find();
23 if($model){
24 $serverSecret=$model->appsecret;
25 }else{
26 return false;
27 }
28
29 //验证签名
30 ksort($args); //按数组的键排序
31 $sign = '';
32
33 foreach($args as $k => $v)
34 {
35 $sign .= $k . '=' . $v;
36 }
37 $sign = MD5($serverSecret.$sign.$serverSecret); //加密
38 if($sign == $signature)
39 {
40 return true;
41 }
42 return false;
43 }
总结:
- 接口调用方和接口提供方约定好统一的参数加密算法
- 接口调用方在调用时把加密后的_sign放在参数中去请求接口
- 接口提供方接到响应后,判断时间戳是不是在有效时间内(这个时间间隔根据你的安全范围可以是10分钟,5分钟,20秒等,过期失效,前提是需要保证接口提供方和调用方的服务器时间为准确的网络同步时间)
- 把参数中除了_sign以外的参数进行加密,然后把加密结果和传过来的_sign比较,相同则执行调用请求。