如何写出安全的API接口(参数加密+超时处理+私钥验证+Https)

举报
Lansonli 发表于 2021/09/28 23:06:09 2021/09/28
【摘要】 上篇文章说到接口安全的设计思路,如果没有看到上篇博客,建议看完再来看这个。 通过园友们的讨论,以及我自己查了些资料,然后对接口安全做一个相对完善的总结,承诺给大家写个demo,今天一并放出。 对于安全也是相对的,下面我来根据安全级别分析   1.完全开放的接口 有没有这样的接口,谁都可以调用,谁都可以访问,不受时间空...

上篇文章说到接口安全的设计思路,如果没有看到上篇博客,建议看完再来看这个。

通过园友们的讨论,以及我自己查了些资料,然后对接口安全做一个相对完善的总结,承诺给大家写个demo,今天一并放出。

对于安全也是相对的,下面我来根据安全级别分析

 

1.完全开放的接口

有没有这样的接口,谁都可以调用,谁都可以访问,不受时间空间限制,只要能连上互联网就能调用,毫无安全可言。

实话说,这样的接口我们天天都在接触,你查快递,你查天气预报,你查飞机,火车班次等,这些都是有公共的接口。

我把这称之为裸奔时代。代码如下:


  
  1. /// <summary>
  2. /// 接口对外公开
  3. /// </summary>
  4. /// <returns></returns>
  5. [HttpGet]
  6. [Route("NoSecure")]
  7. public HttpResponseMessage NoSecure(int age)
  8. {
  9. var result = new ResultModel<object>()
  10. {
  11. ReturnCode = 0,
  12. Message = string.Empty,
  13. Result = string.Empty
  14. };
  15. var dataResult = stulist.Where(T => T.Age == age).ToList();
  16. result.Result = dataResult;
  17. return GetHttpResponseMessage(result);
  18. }

 

2.接口参数加密(基础加密)

 你写个接口,你只想让特定的调用方使用,你把这些调用的人叫到一个小屋子,给他们宣布说我这里有个接口只打算给你们用,我给你们每人一把钥匙,你们用的时候拿着这把钥匙即可。

这把钥匙就是我上文说到的参数加密规则,有了这个规则就能调用。

这有安全问题啊,这里面的某个成员如果哪个不小心丢了钥匙或者被人窃取,掌握钥匙的人是不是也可以来掉用接口了呢?而且他可以复制很多钥匙给不明不白的人用。

相当于有人拿到了你的请求链接,如果业务没有对链接唯一性做判断(实际上业务逻辑通常不会把每次请求的加密签名记录下来,所以不会做唯一性判断),就会被重复调用,有一定安全漏洞,怎么破?先看这个场景的代码,然后继续往下看!


  
  1. /// <summary>
  2. /// 接口加密
  3. /// </summary>
  4. /// <returns></returns>
  5. [HttpGet]
  6. [Route("SecureBySign")]
  7. public HttpResponseMessage SecureBySign([FromUri]int age, long _timestamp, string appKey, string _sign)
  8. {
  9. var result = new ResultModel<object>()
  10. {
  11. ReturnCode = 0,
  12. Message = string.Empty,
  13. Result = string.Empty
  14. };
  15. #region 校验签名是否合法
  16. var param = new SortedDictionary<string, string>(new AsciiComparer());
  17. param.Add("age", age.ToString());
  18. param.Add("appKey", appKey);
  19. param.Add("_timestamp", _timestamp.ToString());
  20. string currentSign = SignHelper.GetSign(param, appKey);
  21. if (_sign != currentSign)
  22. {
  23. result.ReturnCode = -2;
  24. result.Message = "签名不合法";
  25. return GetHttpResponseMessage(result);
  26. }
  27. #endregion
  28. var dataResult = stulist.Where(T => T.Age == age).ToList();
  29. result.Result = dataResult;
  30. return GetHttpResponseMessage(result);
  31. }

 

3.接口参数加密+接口时效性验证(一般达到这个级别已经非常安全了)

继上一步,你发现有不明不白的人调用你的接口,你很不爽,随即把真正需要调用接口的人又叫来,告诉他们每天给他们换一把钥匙。和往常一样,有个别伙伴的钥匙被小偷偷走了,小偷煞费苦心,经过数天的踩点观察,准备在一个月黑风高的夜晚动手。拿出钥匙,捣鼓了半天也无法开启你的神圣之门,因为小偷不知道你天天都在换新钥匙。

小偷不服,经过一段时间琢磨,小偷发现了你们换钥匙的规律。在一次获得钥匙之后,不加思索,当天就动手了,因为他知道他手里的钥匙在第二天你更换钥匙后就失效了。

结果,小偷如愿。怎么破?先看这个场景的代码,然后继续往下看!


  
  1. /// <summary>
  2. /// 接口加密并根据时间戳判断有效性
  3. /// </summary>
  4. /// <returns></returns>
  5. [HttpGet]
  6. [Route("SecureBySign/Expired")]
  7. public HttpResponseMessage SecureBySign_Expired([FromUri]int age, long _timestamp, string appKey, string _sign)
  8. {
  9. var result = new ResultModel<object>()
  10. {
  11. ReturnCode = 0,
  12. Message = string.Empty,
  13. Result = string.Empty
  14. };
  15. #region 判断请求是否过期---假设过期时间是20秒
  16. DateTime requestTime = GetDateTimeByTicks(_timestamp);
  17. if (requestTime.AddSeconds(20) < DateTime.Now)
  18. {
  19. result.ReturnCode = -1;
  20. result.Message = "接口过期";
  21. return GetHttpResponseMessage(result);
  22. }
  23. #endregion
  24. #region 校验签名是否合法
  25. var param = new SortedDictionary<string, string>(new AsciiComparer());
  26. param.Add("age", age.ToString());
  27. param.Add("appKey", appKey);
  28. param.Add("_timestamp", _timestamp.ToString());
  29. string currentSign = SignHelper.GetSign(param, appKey);
  30. if (_sign != currentSign)
  31. {
  32. result.ReturnCode = -2;
  33. result.Message = "签名不合法";
  34. return GetHttpResponseMessage(result);
  35. }
  36. #endregion
  37. var dataResult = stulist.Where(T => T.Age == age).ToList();
  38. result.Result = dataResult;
  39. return GetHttpResponseMessage(result);
  40. }

 

4.接口参数加密+时效性验证+私钥(达到这个级别安全性固若金汤)

 继上一步,你发现道高一尺魔高一丈,仍然有偷盗事情发生。咋办呢?你打算下血本,给每个人配一把钥匙的基础上,再给每个人发个暗号,即使钥匙被小偷弄去了,小偷没有暗号,任然无法如愿。即使小偷真正的如愿,这样也很容易定位是谁的暗号泄漏问题,找到问题根源,只需要给当前这个人换下钥匙就行了,不用大动干戈。

但这个并不是万无一失的,因为钥匙和暗号毕竟还有可能被小偷搞到。代码如下:


  
  1. /// <summary>
  2. /// 接口加密并根据时间戳判断有效性而且带着私有key校验
  3. /// </summary>
  4. /// <returns></returns>
  5. [HttpGet]
  6. [Route("SecureBySign/Expired/KeySecret")]
  7. public HttpResponseMessage SecureBySign_Expired_KeySecret([FromUri]int age, long _timestamp, string appKey, string _sign)
  8. {
  9. //key集合,这里随便弄两个测试数据
  10. //如果调用方比较多,需要审核授权,根据一定的规则生成key把这些数据存放在数据库中,如果功能扩展开来,可以针对不同的调用方做不同的功能权限管理
  11. //在调用接口时动态从库里取,每个调用方在调用时带上他的key,调用方一般把自己的key放到网站配置中
  12. Dictionary<string, string> keySecretDic = new Dictionary<string, string>();
  13. keySecretDic.Add("key_zhangsan", "D9U7YY5D7FF2748AED89E90HJ88881E6");//张三的key,
  14. keySecretDic.Add("key_lisi", "I9O6ZZ3D7FF2748AED89E90ZB7732M9");//李四的key
  15. var result = new ResultModel<object>()
  16. {
  17. ReturnCode = 0,
  18. Message = string.Empty,
  19. Result = string.Empty
  20. };
  21. #region 判断请求是否过期---假设过期时间是20秒
  22. DateTime requestTime = GetDateTimeByTicks(_timestamp);
  23. if (requestTime.AddSeconds(20) < DateTime.Now)
  24. {
  25. result.ReturnCode = -1;
  26. result.Message = "接口过期";
  27. return GetHttpResponseMessage(result);
  28. }
  29. #endregion
  30. #region 根据appkey获取key值
  31. string secret = keySecretDic.Where(T => T.Key == appKey).FirstOrDefault().Value;
  32. #endregion
  33. #region 校验签名是否合法
  34. var param = new SortedDictionary<string, string>(new AsciiComparer());
  35. param.Add("age", age.ToString());
  36. param.Add("appKey", appKey);
  37. param.Add("appSecret", secret);//把secret加入进行加密
  38. param.Add("_timestamp", _timestamp.ToString());
  39. string currentSign = SignHelper.GetSign(param, appKey);
  40. if (_sign != currentSign)
  41. {
  42. result.ReturnCode = -2;
  43. result.Message = "签名不合法";
  44. return GetHttpResponseMessage(result);
  45. }
  46. #endregion
  47. var dataResult = stulist.Where(T => T.Age == age).ToList();
  48. result.Result = dataResult;
  49. return GetHttpResponseMessage(result);
  50. }

 

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下载

 

注:demo不能直接运行,需要把两个web项目配置到iis中,api代表接口提供方,他的主域需要配置到business的webconfig中,在浏览器地址栏分别请求business中的各个调用接口方法来实现接口调用。

1、如果想验证参数错误,需要在请求接口时打个断点把接口url取出,篡改url参数,然后在浏览器中模拟请求

2、如果想验证接口超时,需要在请求接口时打个断点把接口url取出,然后等到了超时时间,然后在浏览器中模拟请求

3、如果想验证私钥错误,需要在请求接口时打个断点把接口url取出,然后修改business的私钥配置,然后在浏览器中模拟请求

文章来源: lansonli.blog.csdn.net,作者:Lansonli,版权归原作者所有,如需转载,请联系作者。

原文链接:lansonli.blog.csdn.net/article/details/105695783

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。