在前一篇文章中,主要討論了使用HTTP基本認證的方法,由於HTTP基本認證的方式決定了它在安全性方面存在很大的問題,因此接下來看看另外一種驗證的方式:digest authentication,即摘要認證。api
在基本認證的方式中,主要的安全問題來自於用戶信息的明文傳輸,而在摘要認證中,主要經過一些手段避免了此問題,大大增長了安全性。安全
下圖爲摘要驗證的驗證原理流程圖。async
下面大體看一下這部分的驗證流程:ide
摘要驗證主要就是經過上面的HASH比較的步驟避免掉了基本驗證中的安全性問題。this
須要注意的是,若是須要IIS支持摘要驗證,須要把IIS摘要驗證的特性勾上。spa
在理解了摘要驗證的原理以後,只須要用代碼實現便可。code
判斷nonce是否過時的方法。orm
public static bool IsValid(string nonce, string nonceCount) { Tuple<int, DateTime> cachedNonce = null; nonces.TryGetValue(nonce, out cachedNonce); if (cachedNonce != null) // nonce is found { // nonce count is greater than the one in record if (Int32.Parse(nonceCount) > cachedNonce.Item1) { // nonce has not expired yet if (cachedNonce.Item2 > DateTime.Now) { // update the dictionary to reflect the nonce count just received in this request nonces[nonce] = new Tuple<int, DateTime>(Int32.Parse(nonceCount), cachedNonce.Item2); // Every thing looks ok - server nonce is fresh and nonce count seems to be // incremented. Does not look like replay. return true; } } } return false; } 判斷nonce是否過時的代碼
下面爲摘要驗證明現的核心方法server
namespace DigestAuthentication { public class AuthenticationHandler : DelegatingHandler { protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { try { var headers = request.Headers; if (headers.Authorization != null) { Header header = new Header(request.Headers.Authorization.Parameter, request.Method.Method); if (Nonce.IsValid(header.Nonce, header.NounceCounter)) { // Just assuming password is same as username for the purpose of illustration string password = header.UserName; string ha1 = String.Format("{0}:{1}:{2}", header.UserName, header.Realm, password).ToMD5Hash(); string ha2 = String.Format("{0}:{1}", header.Method, header.Uri).ToMD5Hash(); string computedResponse = String .Format("{0}:{1}:{2}:{3}:{4}:{5}", ha1, header.Nonce, header.NounceCounter, header.Cnonce, "auth", ha2).ToMD5Hash(); if (String.CompareOrdinal(header.Response, computedResponse) == 0) { // digest computed matches the value sent by client in the response field. // Looks like an authentic client! Create a principal. var claims = new List<Claim> { new Claim(ClaimTypes.Name, header.UserName), new Claim(ClaimTypes.AuthenticationMethod, AuthenticationMethods.Password) }; var principal = new ClaimsPrincipal(new[] { new ClaimsIdentity(claims, "Digest") }); Thread.CurrentPrincipal = principal; if (HttpContext.Current != null) HttpContext.Current.User = principal; } } } var response = await base.SendAsync(request, cancellationToken); if (response.StatusCode == HttpStatusCode.Unauthorized) { response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue("Digest", Header.UnauthorizedResponseHeader.ToString())); } return response; } catch (Exception) { var response = request.CreateResponse(HttpStatusCode.Unauthorized); response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue("Digest", Header.UnauthorizedResponseHeader.ToString())); return response; } } } } 摘要驗證明現的核心方法
實現完成後,使用摘要驗證只須要在對應的方法加上[Authorize]屬性標籤便可。blog
摘要驗證很好地解決了使用基本驗證所擔憂的安全性問題。
可是永遠沒有絕對的安全,當用戶使用字典進行窮舉破解時,仍是會存在一些被破解的隱患。