使用过滤器对mvc api接口安全加密
2021-02-18 17:16
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; using 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 */ ////// api接口加密身份验证过滤器 /// public class VerificationFilters : IAuthorizationFilter { /// /// 接口签名验证 /// /// 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; } //是否合法判断 SortedDictionarystring, string> sortedDictionary = new SortedDictionarystring, 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"); } } }
调用检证接口示例
////// /// /// 身份码 /// 验证码 /// 时间 /// 角色 /// 设备码 /// 版本 /// 数据 /// [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); } SortedDictionarystring, string> sortedDictionary = new SortedDictionarystring, 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(); }
源码下载地址 https://files.cnblogs.com/files/fengmazi/test.rar
技术在于分享,大家共同进步
上一篇:[译] Houdini:也许是你未曾听过的最振奋人心的 CSS 进化
下一篇:3D Computer Grapihcs Using OpenGL - 07 Passing Data from Vertex to Fragment Shader
文章标题:使用过滤器对mvc api接口安全加密
文章链接:http://soscw.com/index.php/essay/57132.html