web api越來越火,因為它的跨平臺,因為它的簡單,因為它支持xml,json等流行的數據協議,我們在開發基于面向服務的API時,有個問題一直在困擾著我們,那就是數據的安全,請求的安全,一般所說的安全也無非就是請求的防篡改和請求的防復用,例如,你向API發一個查詢用戶賬戶的請求,在這個過程中,你可能要傳遞用戶ID,用戶所在項目ID等,而現在攔截工具如此盛行,很容易就可以把它的請求攔截,然后篡改,再轉發,這樣你的API就是不安全的,而對于訂單,賬戶模塊這種糟糕的API設計更是致命的,可能引起的損失是不可預計的,是災難性的,拍拍腦子想想就知道,你向項目提現10元,我把請求攔截把10改成100000,那么這個將是一個災難!
防篡改
一般使用的方式就是把參數拼接,加上雙方約定的“密鑰”,加上你的當前項目AppKey,做一次MD5加密,這個過程生成的字符串我們一般稱為密文,而對應的可見參數我們叫明文,其中明文和密文用于網絡傳輸,而密鑰存儲在本地和服務器,不能進行傳輸!
防復用
一般請求,被重復的使用,也是正常的,就上面的方式進行加密,就無法解決防復用的問題,這時我們需要在客戶端和服務端分別生成UTC的時間戳,這個UTC是防止你的客戶端與服務端不在同一個時區,呵呵,然后把時間戳timestamp拼在密文里就可以了,至于防復用的有效性,我們可以自定義,當然大叔定義的是秒,即同一秒內,請求可以重復發送。
大叔API安全結構圖
web api核心安全校驗代碼片斷
代碼供大家參考和學習,正式的項目可以根據自己公司的需要去設計
/// <summary> /// 功能:api數據安全性驗證 /// 校驗方式:ciphertext=md5(form鍵的值拼接+timestamp+passkey),服務端用接收到的表單數據與時間戳和自己的passkey進行md5生成,最后比較值是否一致 /// passkey為私鑰,不用于網絡傳遞,你可以將它與appKey進行關聯,appKey用來傳遞,服務器根據appKey去數據庫里取對應的passkey然后進行比較 /// 功能:請求唯一性,防偽造性 /// timestamp:UTC時間戳,不用于網絡傳遞,在客戶端調用服務器時,服務器也生成yyyyMMddhhmmss的時間戳,然后進行計算,看是否過期 /// </summary> [AttributeUsage(AttributeTargets.Method)] public class ApiValidateFilter : ActionFilterAttribute { public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext) { #region 初始化 var context = (HttpContextBase)actionContext.Request.Properties["MS_HttpContext"];//獲取傳統context var request = context.Request;//定義傳統request對象 var paramStr = new StringBuilder(); var coll = new NameValueCollection(); if (request.HttpMethod.ToLower() == "get") coll = request.QueryString; else coll = request.Form; #endregion #region 解析XML配置文件 var config = CacheConfigFile.ConfigFactory.Instance.GetConfig<ApiValidateModelConfig>().ApiValidateModelList.FirstOrDefault(i => i.AppKey == coll["AppKey"]); if (config == null) { actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden) { Content = new StringContent("AppKey不是合并的,請先去組織生成有效的Key", Encoding.GetEncoding("UTF-8")) }; base.OnActionExecuting(actionContext); } if (config.ExpireDate < DateTime.Now) { actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden) { Content = new StringContent("AppKey不是合并的,密鑰已過期", Encoding.GetEncoding("UTF-8")) }; base.OnActionExecuting(actionContext); } #endregion #region 驗證算法 var keys = new List<string>(); foreach (string param in coll.Keys) { if (!string.IsNullOrEmpty(param)) { keys.Add(param.ToLower()); } } keys.Sort(); foreach (string p in keys) { if (p != "ciphertext") { if (!string.IsNullOrEmpty(coll[p])) { paramStr.Append(coll[p]); } } } paramStr.Append(DateTime.Now.ToUniversalTime().ToString("yyyyMMddHHmmss")); paramStr.Append(config.PassKey); #endregion if (Lind.DDD.Utils.Encryptor.Utility.EncryptString(paramStr.ToString(), Lind.DDD.Utils.Encryptor.Utility.EncryptorType.MD5) != request["cipherText"]) { actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden) { Content = new StringContent("驗證失敗,請求非法", Encoding.GetEncoding("UTF-8")) }; } base.OnActionExecuting(actionContext); } }
在上的配置項大叔把它存儲到的XML里,使用的是大叔自己封裝的XML緩存組件CacheConfigFile,文件第一次訪問會加載到內存,下次使用直接從內存返回,而當文件修改后,文件的最后更新時間發生變化,這時緩存過期,在生產緩存時,還是采用了單例模式,
這個在大叔框架里經常被看到,呵呵。
文章列表