如何使用签名保证ASP.NET MVC OR WEBAPI的接口安全

 更新时间:2021年04月28日 09:27:50   作者:Agile.Zhou  
这篇文章主要介绍了如何使用签名保证ASP.NET MVC OR WEBAPI的接口安全,帮助大家更好的理解和学习使用.net技术,感兴趣的朋友可以了解下

当我们开发一款App的时候,App需要跟后台服务进行通信获取或者提交数据。如果我们没有完善的安全机制则很容易被别用心的人伪造请求而篡改数据。
所以我们需要使用某种安全机制来保证请求的合法。现在最常用的办法是给每个http请求添加一个签名,服务端来验证签名的合法性,如果签名合法则执行响应的操作,如果签名非法则直接拒绝请求。

签名算法

签名算法一般都使用Hash散列算法,常用的有MD5,SHA系列算法。这些算法可以根据不同的输入,计算出不同的结果,而且碰撞的概率很低。
签名算法跟加密算法不是一回事。很多同学都会说使用MD5加密一下,其实这是错误的。签名算法不能恢复原来的数据,因为它本身并不包含原来数据的信息。
而加密方法不同,加密方法是可以根据加密结果重新推算出原来的数据的。
HMAC SHA作为一种更加安全的签名算法,使用一个Key来影响签名的结果。这样同样的输入配合不同的Key可以得出不同的签名,更加安全。

 public static string HmacSHA256(string secretKey,string plain)
        {
            var keyBytes = Encoding.UTF8.GetBytes(secretKey);
            var plainBytes = Encoding.UTF8.GetBytes(plain);

            using (var hmacsha256 = new HMACSHA256(keyBytes))
            {
                var sb = new StringBuilder();
                var hashValue = hmacsha256.ComputeHash(plainBytes);
                foreach (byte x in hashValue)
                {
                    sb.Append(String.Format("{0:x2}", x));
                }
                return sb.ToString();
            }
        }

签名的参数

有了签名算法,那么我们签名的内容哪里来呢?
一般我们使用http请求的queryString然后加上时间戳还有随机数来作为签名的参数。

 public static string MakeSignPlain(SortedDictionary<string,string> queryString,string time,string random )
        {
            var sb = new StringBuilder();
            foreach (var keyValue in queryString)
            {
                sb.AppendFormat("{0}={1}&", keyValue.Key, keyValue.Value);
            }
            if (sb.Length>1)
            {
                sb.Remove(sb.Length - 1, 1);
            }
            sb.Append(time);
            sb.Append(random);

            return sb.ToString().ToUpper();
        }

验证签名

验证签名就是简单的比较服务端生产的签名跟客户端生产的签名是否一直。
要注意的一点是最好验证下时间戳,跟服务端时间比较前后不能相差5分钟。这也是一个简单的防Replay Attack的手段。

 public static bool Valid(string requestSign,string signPlain,string time, string secretKey)
        {
            if (string.IsNullOrEmpty(time)||string.IsNullOrEmpty(requestSign)||string.IsNullOrEmpty(signPlain))
            {
                return false;
            }
            //is in range
            var now = DateTime.Now;
            long requestTime =0;
            if (long.TryParse(time,out requestTime))
            {
                var max = now.AddMinutes(5).ToString("yyyyMMddHHmmss");
                var min = now.AddMinutes(-5).ToString("yyyyMMddHHmmss");
                if (!(long.Parse(max) >= requestTime && long.Parse(min) <= requestTime))
                {
                    return false;
                }
              
            }
            else
            {
                return false;
            }

            //hashmac
            var sign = Encryption.HmacSHA256(secretKey, signPlain);

            return requestSign.Equals(sign, StringComparison.CurrentCultureIgnoreCase);
        }

ApiController基类

有了上面这些铺垫我们就可以在基类完成签名的验证了。客户端需要把上面提到的时间戳,随机数,签名和客户端的ID放入http请求的headers里面。
我们在基类的OnActionExecuting里取出这些数据组合成签名的参数,然后根据客户端ID获取签名的Key,然后使用同样的签名算法计算签名。并且比较客户端的签名跟服务端的签名是否一致。
这里就不演示了。

预防Replay Attack

预防重放攻击主要有两点:

  • 校验时间戳的范围

时间戳跟服务器时间相差在一个合理的范围内视为合法。

  • 缓存签名

每次请求都去判断下签名是否出现过。如果出现过则视为非法请求。
因为有时间戳跟随机数的存在,所以理论上每次请求的签名是不可能重复的。

客户端调用

这里演示一下C#签名并且调用http接口的代码

 [TestMethod()]
        public void GetUserTest()
        {
            string url = "http://localhost:8090/api/test/GetUser";
            string userId = "A39891D4-6CEF-4538-A562-3A422CA9C17A";
            string appId = "100001";
            string secretKey = "M/vkPOWXgBa7GnRd73t7j+jsKfbZtb+f";
            string rumdon = Guid.NewGuid().ToString();
            string time = DateTime.Now.ToString("yyyyMMddHHmmss");

            //make signture plain text
            var sortDict = new SortedDictionary<string, string>()
            {
                {"userId",userId }
            };
            var signPlain = new StringBuilder();
            foreach (var keyValue in sortDict)
            {
                signPlain.AppendFormat("{0}={1}&", keyValue.Key, keyValue.Value);
            }
            if (signPlain.Length > 1)
            {
                //remove last &
                signPlain.Remove(signPlain.Length - 1, 1);
            }
            signPlain.Append(time);
            signPlain.Append(random);

            Console.WriteLine("sign plain:{0}", signPlain.ToString().ToUpper());
            //make sign
            var sign = Encryption.HmacSHA256(secretKey, signPlain.ToString().ToUpper());
            Console.WriteLine("sign:{0}", sign);

            string requestUrl = string.Format("{0}?{1}={2}", url, "userId", userId);
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUrl);
            request.Method = "GET";
            //add headers
            request.Headers.Add("time", time);
            request.Headers.Add("appId", appId);
            request.Headers.Add("random", random);
            request.Headers.Add("sign", sign);
            //
            //start request
            try
            {
                using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
                {
                    var responseStream = response.GetResponseStream();
                    if (responseStream != null)
                    {
                        using (StreamReader reader = new StreamReader(responseStream))
                        {
                            var content = reader.ReadToEnd();

                            Console.WriteLine(content);
                        }
                    }
                }
            }
            catch (WebException ex)
            {
                using (HttpWebResponse response = (HttpWebResponse)ex.Response)
                {
                    var responseStream = response.GetResponseStream();
                    if (responseStream != null)
                    {
                        using (StreamReader reader = new StreamReader(responseStream))
                        {
                            var content = reader.ReadToEnd();
                            Console.WriteLine(content);
                        }
                    }
                }
            }
        }

以上就是如何使用签名保证ASP.NET MVC OR WEBAPI的接口安全的详细内容,更多关于用签名保证ASP.NET MVC OR WEBAPI的接口安全的资料请关注脚本之家其它相关文章!

相关文章

  • asp.C#实现图片文件与base64string编码解码

    asp.C#实现图片文件与base64string编码解码

    前些天在opera论坛里面当了个flashblocker脚本文件,顾名思义,就是把网页中的flash给过滤了,过滤之后呢就会在原位置显示一张图片,以前用firefox时的flash过滤插件也是这样,而且显示的图片也一样,一样的难看,于是就想换换它。
    2010-03-03
  • 用.Net的File控件上传文件的解决方案

    用.Net的File控件上传文件的解决方案

    用.Net的File控件上传文件的解决方案...
    2007-04-04
  • Asp.net core 使用SignalR推送消息过程详解

    Asp.net core 使用SignalR推送消息过程详解

    ASP.NET Core SignalR 是一个开放源代码库,可用于简化向应用添加实时 Web 功能。 实时 Web 功能使服务器端代码能够将内容推送到客户端,本文重点给大家介绍Asp.net core 使用SignalR推送消息,感兴趣的朋友一起看看吧
    2022-03-03
  • ASP.net Core微信平台开发配置Token

    ASP.net Core微信平台开发配置Token

    这篇文章主要为大家介绍了ASP.net Core微信平台开发配置Token有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-04-04
  • 图析ASP.NET Core引入gRPC服务模板

    图析ASP.NET Core引入gRPC服务模板

    这篇文章主要介绍了图析ASP.NET Core引入gRPC服务模板的过程,目的就是使记录尽可能的详细,尽可能用通俗易懂的语言来进行描述,让大家能用起来。在asp.net core3.0中把grpc服务作为第一等公民进行支持,所以有需要的朋友可以了解下
    2019-04-04
  • ASP.NET 中文显示之两种解决方法

    ASP.NET 中文显示之两种解决方法

    作者刚开始写ASP+程序时候碰到的第一个比较大的问题就是中文显示问题,运行后发现ASP+从数据库中读出来的中文全部变成了?????,有点类似jsp中的这个频率出现最高的中文显示问题了,查了资料发现有种方法可以轻松解决中文问题。
    2008-05-05
  • asp.net GridView 中增加记录的方法

    asp.net GridView 中增加记录的方法

    在 ASP.NET 2.0 中,GridView 支持修改/删除记录,但却不支持新增记录的功能(个人感觉是 GridVew 的一大缺憾,估计在下一版本中会加入此功能)
    2009-03-03
  • updatepanel用法之triggers使用示例

    updatepanel用法之triggers使用示例

    triggers有的两种触发器asyncpostbacktrigger和postbacktrigger,下面是使用示例,需要的朋友可以参考下
    2014-03-03
  • 使用本机IIS Express开发Asp.Net Core应用图文教程

    使用本机IIS Express开发Asp.Net Core应用图文教程

    IIS Express是一个Mini版的IIS,能够支持所有的Web开发任务,本篇经验将和大家介绍使用自定义主机名来访问运行在IIS Express上的站点程序的方法,希望对大家的工作和学习有所帮助
    2023-06-06
  • Asp.Mvc 2.0用户服务器验证实例讲解(4)

    Asp.Mvc 2.0用户服务器验证实例讲解(4)

    这篇文章主要介绍了Asp.Mvc 2.0实现用户服务器验证功能,需要的朋友可以参考下
    2015-08-08

最新评论