使用過濾器對mvc api接口安全加密
阿新 • • 發佈:2018-01-23
display 控制 aspnet 示例 .com res 過度設計 sys req
asp.net api接口安全
安全要求:
b1.防偽裝攻擊(案例:在公共網絡環境中,第三方 有意或惡意 的調用我們的接口)
b2.防篡改攻擊(案例:在公共網絡環境中,請求頭/查詢字符串/內容 在傳輸過程被修改)
b3.防重放攻擊(案例:在公共網絡環境中,請求被截獲,稍後被重放或多次重放)
b4.防數據信息泄漏(案例:截獲用戶登錄請求,截獲到賬號、密碼等)
設計原則
1.輕量級
2.適合於異構系統(跨操作系統、多語言簡易實現)
3.易於開發
4.易於測試
5.易於部署
6.滿足接口安全需求(滿足b1 b2 b3要求),無過度設計。
其它:接口安全要求b4部分,主要針對目前用戶中心的登錄接口
設計原則是:使用HTTPS安全協議 或 傳輸內容使用非對稱加密,目前我們采用的後者
適用範圍
1.所有寫操作接口(增、刪、改 操作)
2.非公開的讀接口(如:涉密/敏感/隱私 等信息)
api 接口安全驗證 過濾器代碼
using System; using System.Collections.Generic; using System.IO; using System.Text; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using test.Models;View Codeusing TEST.Common; namespace test.Filters { /* * 第一步:登錄系統,獲取appkey * 第二步:http請求頭headers加入字段appid,sign,timestamp三個字段,appid:用戶名,sign:簽名值(後面詳細解說),timestamp:當前時間戳 ,如:2017/12/12 17:11:9 值為: 1513069869 * 第三步:把業務請求的參數傳入到http消息體Body(x-www-form-urlencoded) * 第四步:計算sign值,對除簽名外的所有請求參數按 參數名+值 做的升序排列,value值無需編碼 * 整個流程示例如下: * appkey=D9U7YY5D7FF2748AED89E90HJ88881E6 (此參數不需要排序,而是加在文本開頭和結尾處) * headers參數如下 * appid=user1 * sign=??? * timestamp=1513069265 * body參數如下 * par1=52.8 * par2=這是一個測試參數 * par3=852 * * 1)按 參數名+值 以升序排序,結果:appiduser1par152.8par2這是一個測試參數par3852timestamp1513069265 * 2)在本文開頭和結尾加上登錄時獲取的appkey 結果為:D9U7YY5D7FF2748AED89E90HJ88881E6appiduser1par152.8par2這是一個測試參數par3852timestamp1513069265D9U7YY5D7FF2748AED89E90HJ88881E6 * 3)對此文本進行md5 32位 大寫 加密,此時就是sign值 結果為:B44C81F3DF4D5E8A614C84977D33E8D2*/ /// <summary> /// api接口加密身份驗證過濾器 /// </summary> public class VerificationFilters : IAuthorizationFilter { /// <summary> /// 接口簽名驗證 /// </summary> /// <param name="context"></param> public void OnAuthorization(AuthorizationFilterContext context) { ModelResult modelResult = new ModelResult(); //參數判斷 if (!context.HttpContext.Request.Headers.ContainsKey("appid")) { modelResult.code = -1; modelResult.message = "缺少appid參數!"; JsonResult json = new JsonResult(modelResult); context.Result = json; } else if (!context.HttpContext.Request.Headers.ContainsKey("sign")) { modelResult.code = -1; modelResult.message = "缺少sign參數!"; JsonResult json = new JsonResult(modelResult); context.Result = json; } else if (!context.HttpContext.Request.Headers.ContainsKey("timestamp")) { modelResult.code = -1; modelResult.message = "缺少timestamp參數!"; JsonResult json = new JsonResult(modelResult); context.Result = json; } else { string appid = context.HttpContext.Request.Headers["appid"]; string sign = context.HttpContext.Request.Headers["sign"]; string timestamp = context.HttpContext.Request.Headers["timestamp"]; DateTime requestTime = DateTimeHelper.GetTime(timestamp); // 接口過期 int apiExpiry = 20; if (requestTime.AddSeconds(apiExpiry) < DateTime.Now) { modelResult.code = -3; modelResult.message = "接口過期!"; JsonResult json = new JsonResult(modelResult); context.Result = json; } else { //從數據庫或緩存查找對應的appkey, string appkey = "fdsafdsafdsafasdfasdf"; if (!string.IsNullOrEmpty(appkey)) { modelResult.code = -4; modelResult.message = "appid不存在!"; JsonResult json = new JsonResult(modelResult); context.Result = json; return; } //是否合法判斷 SortedDictionary<string, string> sortedDictionary = new SortedDictionary<string, string>(); sortedDictionary.Add("appid", appid); sortedDictionary.Add("timestamp", timestamp); //獲取post數據,並排序 Stream stream = context.HttpContext.Request.Body; byte[] buffer = new byte[context.HttpContext.Request.ContentLength.Value]; stream.Read(buffer, 0, buffer.Length); string content = Encoding.UTF8.GetString(buffer); context.HttpContext.Request.Body = new MemoryStream(buffer); if (!String.IsNullOrEmpty(content)) { string postdata = System.Web.HttpUtility.UrlDecode(content); string[] posts = postdata.Split(new char[] { ‘&‘ }); foreach (var item in posts) { string[] post = item.Split(new char[] { ‘=‘ }); sortedDictionary.Add(post[0], post[1]); } } //拼接參數,並在開頭和結尾加上key StringBuilder sb = new StringBuilder(appkey); foreach (var item in sortedDictionary) { sb.Append(item.Key).Append(item.Value); } sb.Append(appkey); if (sign != CryptographyHelper.Md5_Encryption(sb.ToString())) { modelResult.code = -2; modelResult.message = "簽名不合法!"; JsonResult json = new JsonResult(modelResult); context.Result = json; } } } } } }
使用方式
using Microsoft.AspNetCore.Mvc; using test.Filters; namespace TEST.Controllers { [TypeFilter(typeof(VerificationFilters))] public class HomeController : Controller { //此接口就使用接口驗證過濾器,從控制器上繼承下來的 public IActionResult Index() { return Json("aa"); } //此接口就使用接口驗證過濾器,在action頭標記了過濾器,可單獨作用於action [TypeFilter(typeof(VerificationFilters))] public IActionResult Index2() { return Json("aa"); } } }View Code
調用檢證接口示例
/// <summary> /// /// </summary> /// <param name="appid">身份碼</param> /// <param name="appkey">驗證碼</param> /// <param name="timestamp">時間</param> /// <param name="role">角色</param> /// <param name="identity">設備碼</param> /// <param name="version">版本</param> /// <param name="data">數據</param> /// <returns></returns> [HttpPost] public IActionResult api(string appid, string appkey, int timestamp, int role, int identity, string version, string data) { if (timestamp == 0) { timestamp = DateTimeHelper.ConvertDateTimeInt(DateTime.Now); } SortedDictionary<string, string> sortedDictionary = new SortedDictionary<string, string>(); sortedDictionary.Add("appid", appid); sortedDictionary.Add("timestamp", timestamp.ToString()); sortedDictionary.Add("role", role.ToString()); sortedDictionary.Add("identity", identity.ToString()); sortedDictionary.Add("version", version); string val = ""; try { string[] dataitem = data.Split("\r\n"); foreach (var item in dataitem) { string[] tmp = item.Split(‘=‘); sortedDictionary.Add(tmp[0], tmp[1]); } } catch (Exception) { val = "data數據格式有問題!"; ViewBag.Data = val; return View(); } StringBuilder sb = new StringBuilder(appkey); foreach (var p in sortedDictionary) sb.Append(p.Key).Append(p.Value); sb.Append(appkey); string sign = CryptographyHelper.Md5_Encryption(sb.ToString()); val = $" appid:{appid}\r\n sign:{sign}\r\n timestamp:{timestamp}\r\n {data}"; ViewBag.Data = val; return View(); }View Code
源碼下載地址 https://files.cnblogs.com/files/fengmazi/test.rar
技術在於分享,大家共同進步
使用過濾器對mvc api接口安全加密