如何写出安全的API接口
通過園友們的討論,以及我自己查了些資料,然后對(duì)接口安全做一個(gè)相對(duì)完善的總結(jié),承諾給大家寫個(gè)demo,今天一并放出。
對(duì)于安全也是相對(duì)的,下面我來根據(jù)安全級(jí)別分析
?
1.完全開放的接口
有沒有這樣的接口,誰都可以調(diào)用,誰都可以訪問,不受時(shí)間空間限制,只要能連上互聯(lián)網(wǎng)就能調(diào)用,毫無安全可言。
實(shí)話說,這樣的接口我們天天都在接觸,你查快遞,你查天氣預(yù)報(bào),你查飛機(jī),火車班次等,這些都是有公共的接口。
我把這稱之為裸奔時(shí)代。代碼如下:
/// <summary>
/// 接口對(duì)外公開
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("NoSecure")]
public HttpResponseMessage NoSecure(int age)
{
var result = new ResultModel<object>()
{
ReturnCode = 0,
Message = string.Empty,
Result = string.Empty
};
var dataResult = stulist.Where(T => T.Age == age).ToList();
result.Result = dataResult;
return GetHttpResponseMessage(result);
}
?
2.接口參數(shù)加密(基礎(chǔ)加密)
?你寫個(gè)接口,你只想讓特定的調(diào)用方使用,你把這些調(diào)用的人叫到一個(gè)小屋子,給他們宣布說我這里有個(gè)接口只打算給你們用,我給你們每人一把鑰匙,你們用的時(shí)候拿著這把鑰匙即可。
這把鑰匙就是我上文說到的參數(shù)加密規(guī)則,有了這個(gè)規(guī)則就能調(diào)用。
這有安全問題啊,這里面的某個(gè)成員如果哪個(gè)不小心丟了鑰匙或者被人竊取,掌握鑰匙的人是不是也可以來掉用接口了呢?而且他可以復(fù)制很多鑰匙給不明不白的人用。
相當(dāng)于有人拿到了你的請(qǐng)求鏈接,如果業(yè)務(wù)沒有對(duì)鏈接唯一性做判斷(實(shí)際上業(yè)務(wù)邏輯通常不會(huì)把每次請(qǐng)求的加密簽名記錄下來,所以不會(huì)做唯一性判斷),就會(huì)被重復(fù)調(diào)用,有一定安全漏洞,怎么破?先看這個(gè)場(chǎng)景的代碼,然后繼續(xù)往下看!
?
/// <summary>
/// 接口加密
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("SecureBySign")]
public HttpResponseMessage SecureBySign([FromUri]int age, long _timestamp, string appKey, string _sign)
{
var result = new ResultModel<object>()
{
ReturnCode = 0,
Message = string.Empty,
Result = string.Empty
};
#region 校驗(yàn)簽名是否合法
var param = new SortedDictionary<string, string>(new AsciiComparer());
param.Add("age", age.ToString());
param.Add("appKey", appKey);
param.Add("_timestamp", _timestamp.ToString());
string currentSign = SignHelper.GetSign(param, appKey);
if (_sign != currentSign)
{
result.ReturnCode = -2;
result.Message = "簽名不合法";
return GetHttpResponseMessage(result);
}
#endregion
var dataResult = stulist.Where(T => T.Age == age).ToList();
result.Result = dataResult;
return GetHttpResponseMessage(result);
}
3.接口參數(shù)加密+接口時(shí)效性驗(yàn)證(一般達(dá)到這個(gè)級(jí)別已經(jīng)非常安全了)
繼上一步,你發(fā)現(xiàn)有不明不白的人調(diào)用你的接口,你很不爽,隨即把真正需要調(diào)用接口的人又叫來,告訴他們每天給他們換一把鑰匙。和往常一樣,有個(gè)別伙伴的鑰匙被小偷偷走了,小偷煞費(fèi)苦心,經(jīng)過數(shù)天的踩點(diǎn)觀察,準(zhǔn)備在一個(gè)月黑風(fēng)高的夜晚動(dòng)手。拿出鑰匙,搗鼓了半天也無法開啟你的神圣之門,因?yàn)樾⊥挡恢滥闾焯於荚趽Q新鑰匙。
小偷不服,經(jīng)過一段時(shí)間琢磨,小偷發(fā)現(xiàn)了你們換鑰匙的規(guī)律。在一次獲得鑰匙之后,不加思索,當(dāng)天就動(dòng)手了,因?yàn)樗浪掷锏蔫€匙在第二天你更換鑰匙后就失效了。
結(jié)果,小偷如愿。怎么破?先看這個(gè)場(chǎng)景的代碼,然后繼續(xù)往下看!
/// <summary>
/// 接口加密并根據(jù)時(shí)間戳判斷有效性
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("SecureBySign/Expired")]
public HttpResponseMessage SecureBySign_Expired([FromUri]int age, long _timestamp, string appKey, string _sign)
{
var result = new ResultModel<object>()
{
ReturnCode = 0,
Message = string.Empty,
Result = string.Empty
};
#region 判斷請(qǐng)求是否過期---假設(shè)過期時(shí)間是20秒
DateTime requestTime = GetDateTimeByTicks(_timestamp);
if (requestTime.AddSeconds(20) < DateTime.Now)
{
result.ReturnCode = -1;
result.Message = "接口過期";
return GetHttpResponseMessage(result);
}
#endregion
#region 校驗(yàn)簽名是否合法
var param = new SortedDictionary<string, string>(new AsciiComparer());
param.Add("age", age.ToString());
param.Add("appKey", appKey);
param.Add("_timestamp", _timestamp.ToString());
string currentSign = SignHelper.GetSign(param, appKey);
if (_sign != currentSign)
{
result.ReturnCode = -2;
result.Message = "簽名不合法";
return GetHttpResponseMessage(result);
}
#endregion
var dataResult = stulist.Where(T => T.Age == age).ToList();
result.Result = dataResult;
return GetHttpResponseMessage(result);
}
?
4.接口參數(shù)加密+時(shí)效性驗(yàn)證+私鑰(達(dá)到這個(gè)級(jí)別安全性固若金湯)
?繼上一步,你發(fā)現(xiàn)道高一尺魔高一丈,仍然有偷盜事情發(fā)生。咋辦呢?你打算下血本,給每個(gè)人配一把鑰匙的基礎(chǔ)上,再給每個(gè)人發(fā)個(gè)暗號(hào),即使鑰匙被小偷弄去了,小偷沒有暗號(hào),任然無法如愿,而且這樣很容易定位是誰的暗號(hào)泄漏問題,找到問題根源,只需要給當(dāng)前這個(gè)人換下鑰匙就行了,不用大動(dòng)干戈。
但這個(gè)并不是萬無一失的,因?yàn)殍€匙畢竟還有可能被小偷搞到。代碼如下:
/// <summary>
/// 接口加密并根據(jù)時(shí)間戳判斷有效性而且?guī)е接衚ey校驗(yàn)
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("SecureBySign/Expired/KeySecret")]
public HttpResponseMessage SecureBySign_Expired_KeySecret([FromUri]int age, long _timestamp, string appKey, string _sign)
{
//key集合,這里隨便弄兩個(gè)測(cè)試數(shù)據(jù)
//如果調(diào)用方比較多,需要審核授權(quán),根據(jù)一定的規(guī)則生成key把這些數(shù)據(jù)存放在數(shù)據(jù)庫中,如果功能擴(kuò)展開來,可以針對(duì)不同的調(diào)用方做不同的功能權(quán)限管理
//在調(diào)用接口時(shí)動(dòng)態(tài)從庫里取,每個(gè)調(diào)用方在調(diào)用時(shí)帶上他的key,調(diào)用方一般把自己的key放到網(wǎng)站配置中
Dictionary<string, string> keySecretDic = new Dictionary<string, string>();
keySecretDic.Add("key_zhangsan", "D9U7YY5D7FF2748AED89E90HJ88881E6");//張三的key,
keySecretDic.Add("key_lisi", "I9O6ZZ3D7FF2748AED89E90ZB7732M9");//李四的key
var result = new ResultModel<object>()
{
ReturnCode = 0,
Message = string.Empty,
Result = string.Empty
};
#region 判斷請(qǐng)求是否過期---假設(shè)過期時(shí)間是20秒
DateTime requestTime = GetDateTimeByTicks(_timestamp);
if (requestTime.AddSeconds(20) < DateTime.Now)
{
result.ReturnCode = -1;
result.Message = "接口過期";
return GetHttpResponseMessage(result);
}
#endregion
#region 根據(jù)appkey獲取key值
string secret = keySecretDic.Where(T => T.Key == appKey).FirstOrDefault().Value;
#endregion
#region 校驗(yàn)簽名是否合法
var param = new SortedDictionary<string, string>(new AsciiComparer());
param.Add("age", age.ToString());
param.Add("appKey", appKey);
param.Add("appSecret", secret);//把secret加入進(jìn)行加密
param.Add("_timestamp", _timestamp.ToString());
string currentSign = SignHelper.GetSign(param, appKey);
if (_sign != currentSign)
{
result.ReturnCode = -2;
result.Message = "簽名不合法";
return GetHttpResponseMessage(result);
}
#endregion
var dataResult = stulist.Where(T => T.Age == age).ToList();
result.Result = dataResult;
return GetHttpResponseMessage(result);
}
?
5.接口參數(shù)加密+時(shí)效性驗(yàn)證+私鑰+Https(我把這個(gè)級(jí)別稱之為金鐘罩,世間最安全莫過于此)
繼上一步,我們給傳輸機(jī)制改為Https,這下小偷徹底懵逼了。那么問題來了,Https咋玩兒呢?可以在本地搭個(gè)環(huán)境,參考此文:http://www.cnblogs.com/naniannayue/archive/2012/11/19/2776948.html
?
另:本文的接口是用的MVC WebAPI寫的,完全基于RESTful標(biāo)準(zhǔn)。如對(duì)此不是特別了解可以參考此文:http://www.cnblogs.com/landeanfen/p/5501490.html
?
完整demo下載
?
注:demo不能直接運(yùn)行,需要把兩個(gè)web項(xiàng)目配置到iis中,api代表接口提供方,他的主域需要配置到business的webconfig中,在瀏覽器地址欄分別請(qǐng)求business中的各個(gè)調(diào)用接口方法來實(shí)現(xiàn)接口調(diào)用。
1、如果想驗(yàn)證接口超時(shí),需要在請(qǐng)求接口時(shí)打個(gè)斷點(diǎn)把接口url取出,然后等到了超時(shí)時(shí)間,然后在瀏覽器中模擬請(qǐng)求
2、如果想驗(yàn)證參數(shù)錯(cuò)誤,需要在請(qǐng)求接口時(shí)打個(gè)斷點(diǎn)把接口url取出,篡改url參數(shù),然后在瀏覽器中模擬請(qǐng)求
http://www.cnblogs.com/accumulater/p/6178166.html
總結(jié)
以上是生活随笔為你收集整理的如何写出安全的API接口的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: EF性能优化(一)
- 下一篇: 【收集】常用的cmd命令