zoukankan      html  css  js  c++  java
  • 如何写出安全的API接口

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

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

    1.完全开放的接口

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

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

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

    /// <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.接口参数加密(基础加密)

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

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

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

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

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

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

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

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

    http://www.cnblogs.com/accumulater/p/6178166.html


  • 相关阅读:
    pat 甲级 1065. A+B and C (64bit) (20)
    pat 甲级 1064. Complete Binary Search Tree (30)
    pat 甲级 1010. Radix (25)
    pat 甲级 1009. Product of Polynomials (25)
    pat 甲级 1056. Mice and Rice (25)
    pat 甲级 1078. Hashing (25)
    pat 甲级 1080. Graduate Admission (30)
    pat 甲级 团体天梯 L3-004. 肿瘤诊断
    pat 甲级 1099. Build A Binary Search Tree (30)
    Codeforce 672B. Different is Good
  • 原文地址:https://www.cnblogs.com/hgmyz/p/12352230.html
Copyright © 2011-2022 走看看