[转]ASP.NET Web API(三):安全验证之使用摘要认证(digest authentication)

本文转自:http://www.cnblogs.com/parry/p/ASPNET_MVC_Web_API_digest_authentication.html

前一篇文章中,主要讨论了使用HTTP基本认证的方法,因为HTTP基本认证的方式决定了它在安全性方面存在很大的问题,所以接下来看看另一种验证的方式:digest authentication,即摘要认证。

系列文章列表

ASP.NET Web API(一):使用初探,GET和POST数据 ASP.NET Web API(二):安全验证之使用HTTP基本认证 ASP.NET Web API(三):安全验证之使用摘要认证(digest authentication)

摘要认证原理

在基本认证的方式中,主要的安全问题来自于用户信息的明文传输,而在摘要认证中,主要通过一些手段避免了此问题,大大增加了安全性。

下图为摘要验证的验证原理流程图。

下面大致看一下这部分的验证流程:

  1. 客户端请求 /api/employees;
  2. 服务端返回401未验证的状态,并且在返回的信息中包含了验证方式Digest,realm的值,QOP(quality of protection)只设置成auth,nonce为一串随机值,在下面的请求中会一直使用到,当过了存活期后服务端将刷新生成一个新的nonce值;
  3. 客户端接受到请求返回后,将username:realm:password进行HASH运算,假设运算后的值为HA1。又将请求的路径/api/employees进行HASH运算,假设运算后的值为HA2。再将HA1:nonce:nc:cnonce:qop:HA2进行HASH运算,得到的值放在response中。这里的cnonce为客户端生成的nonce值,而nc用于统计,假设开始时为00000001,下次请求后就变成了00000002,不一定每次都加1,但是后面请求中的nc值肯定大于前一次请求中的nc值。
  4. 服务端收到请求后将验证nonce是否过期,如果过期,那么直接返回401,即第二步的状态。如果没有过期,那么比较nc值,如果比前一次nc值小或者前一次根本没有存储的nc值,那么也将直接返回401状态。如果前面的验证都通过,那么服务端也将按照步骤3中计算最终HASH值的步骤计算出HASH值与客户端的进行比较,然后比较客户端提交过来的HASH值与服务端计算出来的HASH进行比较,不匹配返回401,匹配获取请求的数据并返回状态200。

摘要验证主要就是通过上面的HASH比较的步骤避免掉了基本验证中的安全性问题。

需要注意的是,如果需要IIS支持摘要验证,需要把IIS摘要验证的特性勾上。

摘要验证的实现

在理解了摘要验证的原理之后,只需要用代码实现即可。

判断nonce是否过期的方法。

复制代码
 1         public static bool IsValid(string nonce, string nonceCount)  2  {  3 Tuple<int, DateTime> cachedNonce = null;  4 nonces.TryGetValue(nonce, out cachedNonce);  5  6 if (cachedNonce != null) // nonce is found  7  {  8 // nonce count is greater than the one in record  9 if (Int32.Parse(nonceCount) > cachedNonce.Item1) 10  { 11 // nonce has not expired yet 12 if (cachedNonce.Item2 > DateTime.Now) 13  { 14 // update the dictionary to reflect the nonce count just received in this request 15 nonces[nonce] = new Tuple<int, DateTime>(Int32.Parse(nonceCount), 16  cachedNonce.Item2); 17 18 // Every thing looks ok - server nonce is fresh and nonce count seems to be 19 // incremented. Does not look like replay. 20 return true; 21  } 22  } 23  } 24 25 return false; 26 }
复制代码
判断nonce是否过期的代码

下面为摘要验证实现的核心方法

复制代码
 1 namespace DigestAuthentication
 2 {
 3 public class AuthenticationHandler : DelegatingHandler  4  {  5 protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)  6  {  7 try  8  {  9 var headers = request.Headers; 10 if (headers.Authorization != null) 11  { 12 Header header = new Header(request.Headers.Authorization.Parameter, 13  request.Method.Method); 14 15 if (Nonce.IsValid(header.Nonce, header.NounceCounter)) 16  { 17 // Just assuming password is same as username for the purpose of illustration 18 string password = header.UserName; 19 20 string ha1 = String.Format("{0}:{1}:{2}", header.UserName, header.Realm, 21  password).ToMD5Hash(); 22 23 string ha2 = String.Format("{0}:{1}", header.Method, header.Uri).ToMD5Hash(); 24 25 string computedResponse = String 26 .Format("{0}:{1}:{2}:{3}:{4}:{5}", 27  ha1, header.Nonce, header.NounceCounter, 28 header.Cnonce, "auth", ha2).ToMD5Hash(); 29 30 if (String.CompareOrdinal(header.Response, computedResponse) == 0) 31  { 32 // digest computed matches the value sent by client in the response field. 33 // Looks like an authentic client! Create a principal. 34 var claims = new List<Claim> 35  { 36 new Claim(ClaimTypes.Name, header.UserName), 37 new Claim(ClaimTypes.AuthenticationMethod, AuthenticationMethods.Password) 38  }; 39 40 var principal = new ClaimsPrincipal(new[] { new ClaimsIdentity(claims, "Digest") }); 41 42 Thread.CurrentPrincipal = principal; 43 44 if (HttpContext.Current != null) 45 HttpContext.Current.User = principal; 46  } 47  } 48  } 49 50 var response = await base.SendAsync(request, cancellationToken); 51 52 if (response.StatusCode == HttpStatusCode.Unauthorized) 53  { 54 response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue("Digest", 55  Header.UnauthorizedResponseHeader.ToString())); 56  } 57 58 return response; 59  } 60 catch (Exception) 61  { 62 var response = request.CreateResponse(HttpStatusCode.Unauthorized); 63 response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue("Digest", 64  Header.UnauthorizedResponseHeader.ToString())); 65 66 return response; 67  } 68  } 69  } 70 71 }
复制代码
摘要验证实现的核心方法

实现完成后,使用摘要验证只需要在对应的方法加上[Authorize]属性标签即可。

摘要验证的优缺点

摘要验证很好地解决了使用基本验证所担心的安全性问题。

但是永远没有绝对的安全,当用户使用字典进行穷举破解时,还是会存在一些被破解的隐患。

源码下载

编辑器里怎么找不到上传文件的地方了?我上传到了百度网盘里。

源代码下载

作者:Parry            出处:http://www.cnblogs.com/parry/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 

 
分类: 00.ASP.NET

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值