如何寫出安全的API接口(參數加密+超時處理+私鑰驗證+Https)


原文:http://www.cnblogs.com/codeon/p/6123863.html#3580351

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM