1. 程式人生 > >ASP.NET Web API(三):安全驗證之使用摘要認證(digest authentication)

ASP.NET Web API(三):安全驗證之使用摘要認證(digest authentication)

前一篇文章中,主要討論了使用HTTP基本認證的方法,因為HTTP基本認證的方式決定了它在安全性方面存在很大的問題,所以接下來看看另一種驗證的方式: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]屬性標籤即可。

摘要驗證的優缺點

摘要驗證很好地解決了使用基本驗證所擔心的安全性問題。

但是永遠沒有絕對的安全,當用戶使用字典進行窮舉破解時,還是會存在一些被破解的隱患。

原始碼下載

編輯器裡怎麼找不到上傳檔案的地方了?我上傳到了百度網盤裡。

原始碼下載