HTML5技术

使用签名来保证ASP.NET MVC OR WEBAPI的接口安全 - Agile.Zhou

字号+ 作者:H5之家 来源:H5之家 2016-09-24 17:00 我要评论( )

使用签名来保证ASP.NET MVC OR WEBAPI的接口安全 当我们开发一款App的时候,App需要跟后台服务进行通信获取或者提交数据。如果我们没有完善的安全机制则很容易被别用心的人伪造请求而篡改数据。 所以我们需要使用某种安全机制来保证请求的合法。现在最常用的

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

当我们开发一款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 = ":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); } } } } }

    posted @

     

    1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

    相关文章
    • html5 video.js 使用及兼容所有浏览器 - 小童童

      html5 video.js 使用及兼容所有浏览器 - 小童童

      2016-09-24 15:00

    • Canvas——使用定时器模拟动态加载动画! - li_han

      Canvas——使用定时器模拟动态加载动画! - li_han

      2016-09-23 15:00

    • 本地缓存localstorage使用 - 一只机智的猫

      本地缓存localstorage使用 - 一只机智的猫

      2016-09-23 10:00

    • canvas对象arc函数的使用-遁地龙卷风 - 遁地龙卷风

      canvas对象arc函数的使用-遁地龙卷风 - 遁地龙卷风

      2016-09-07 15:00

    网友点评