上篇文章说到接口安全的设计思路,若是没有看到上篇博客,建议看完再来看这个。html
经过园友们的讨论,以及我本身查了些资料,而后对接口安全作一个相对完善的总结,承诺给你们写个demo,今天一并放出。git
对于安全也是相对的,下面我来根据安全级别分析github
1.彻底开放的接口web
有没有这样的接口,谁均可以调用,谁均可以访问,不受时间空间限制,只要能连上互联网就能调用,毫无安全可言。数据库
实话说,这样的接口咱们每天都在接触,你查快递,你查天气预报,你查飞机,火车班次等,这些都是有公共的接口。api
我把这称之为裸奔时代。代码以下:浏览器
/// <summary>
/// 接口对外公开 /// </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.接口参数加密(基础加密)安全
你写个接口,你只想让特定的调用方使用,你把这些调用的人叫到一个小屋子,给他们宣布说我这里有个接口只打算给大家用,我给大家每人一把钥匙,大家用的时候拿着这把钥匙便可。app
这把钥匙就是我上文说到的参数加密规则,有了这个规则就能调用。ide
这有安全问题啊,这里面的某个成员若是哪一个不当心丢了钥匙或者被人窃取,掌握钥匙的人是否是也能够来掉用接口了呢?并且他能够复制不少钥匙给不明不白的人用。
至关于有人拿到了你的请求连接,若是业务没有对连接惟一性作判断(实际上业务逻辑一般不会把每次请求的加密签名记录下来,因此不会作惟一性判断),就会被重复调用,有必定安全漏洞,怎么破?先看这个场景的代码,而后继续往下看!
/// <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 校验签名是否合法
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.接口参数加密+接口时效性验证(通常达到这个级别已经很是安全了)
继上一步,你发现有不明不白的人调用你的接口,你很不爽,随即把真正须要调用接口的人又叫来,告诉他们天天给他们换一把钥匙。和往常同样,有个别伙伴的钥匙被小偷偷走了,小偷煞费苦心,通过数天的踩点观察,准备在一个月黑风高的夜晚动手。拿出钥匙,捣鼓了半天也没法开启你的神圣之门,由于小偷不知道你每天都在换新钥匙。
小偷不服,通过一段时间琢磨,小偷发现了大家换钥匙的规律。在一次得到钥匙以后,不加思索,当天就动手了,由于他知道他手里的钥匙在次日你更换钥匙后就失效了。
结果,小偷如愿。怎么破?先看这个场景的代码,而后继续往下看!
/// <summary>
/// 接口加密并根据时间戳判断有效性 /// </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 判断请求是否过时---假设过时时间是20秒 DateTime requestTime = GetDateTimeByTicks(_timestamp); if (requestTime.AddSeconds(20) < DateTime.Now) { result.ReturnCode = -1; result.Message = "接口过时"; return GetHttpResponseMessage(result); } #endregion
#region 校验签名是否合法
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.接口参数加密+时效性验证+私钥(达到这个级别安全性固若金汤)
继上一步,你发现道高一尺魔高一丈,仍然有偷盗事情发生。咋办呢?你打算下血本,给每一个人配一把钥匙的基础上,再给每一个人发个暗号,即便钥匙被小偷弄去了,小偷没有暗号,任然没法如愿。即便小偷真正的如愿,这样也很容易定位是谁的暗号泄漏问题,找到问题根源,只须要给当前这我的换下钥匙就好了,不用大动干戈。
但这个并非万无一失的,由于钥匙和暗号毕竟还有可能被小偷搞到。代码以下:
/// <summary>
/// 接口加密并根据时间戳判断有效性并且带着私有key校验 /// </summary>
/// <returns></returns>
[HttpGet] [Route("SecureBySign/Expired/KeySecret")] public HttpResponseMessage SecureBySign_Expired_KeySecret([FromUri]int age, long _timestamp, string appKey, string _sign) { //key集合,这里随便弄两个测试数据 //若是调用方比较多,须要审核受权,根据必定的规则生成key把这些数据存放在数据库中,若是功能扩展开来,能够针对不一样的调用方作不一样的功能权限管理 //在调用接口时动态从库里取,每一个调用方在调用时带上他的key,调用方通常把本身的key放到网站配置中
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 判断请求是否过时---假设过时时间是20秒 DateTime requestTime = GetDateTimeByTicks(_timestamp); if (requestTime.AddSeconds(20) < DateTime.Now) { result.ReturnCode = -1; result.Message = "接口过时"; return GetHttpResponseMessage(result); } #endregion
#region 根据appkey获取key值
string secret = keySecretDic.Where(T => T.Key == appKey).FirstOrDefault().Value; #endregion
#region 校验签名是否合法
var param = new SortedDictionary<string, string>(new AsciiComparer()); param.Add("age", age.ToString()); param.Add("appKey", appKey); param.Add("appSecret", secret);//把secret加入进行加密
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.接口参数加密+时效性验证+私钥+Https(我把这个级别称之为金钟罩,世间最安全莫过于此)
继上一步,咱们给传输机制改成Https,这下小偷完全懵逼了。那么问题来了,Https咋玩儿呢?能够在本地搭个环境,参考此文:http://www.cnblogs.com/naniannayue/archive/2012/11/19/2776948.html
另:本文的接口是用的MVC WebAPI写的,彻底基于RESTful标准。如对此不是特别了解能够参考此文:http://www.cnblogs.com/landeanfen/p/5501490.html
注:demo不能直接运行,须要把两个web项目配置到iis中,api表明接口提供方,他的主域须要配置到business的webconfig中,在浏览器地址栏分别请求business中的各个调用接口方法来实现接口调用。
一、若是想验证参数错误,须要在请求接口时打个断点把接口url取出,篡改url参数,而后在浏览器中模拟请求
二、若是想验证接口超时,须要在请求接口时打个断点把接口url取出,而后等到了超时时间,而后在浏览器中模拟请求
三、若是想验证私钥错误,须要在请求接口时打个断点把接口url取出,而后修改business的私钥配置,而后在浏览器中模拟请求