2013-03-22 68 views
67

我在Angular.js中實現了一個網站,該網站正在訪問ASP.NET WebAPI後端。Angular對Asp.Net WebApi,在服務器上實現CSRF

Angular.js有一些內置的功能來幫助防csrf保護。在每個http請求上,它將查找名爲「XSRF-TOKEN」的cookie並將其作爲名爲「X-XSRF-TOKEN」的頭提交。

這依賴於Web服務器能夠在認證用戶之後設置XSRF-TOKEN cookie,然後檢查傳入請求的X-XSRF-TOKEN標頭。

Angular documentation狀態:

要利用這一點,你的服務器需要設置令牌叫XSRF-TOKEN的第一個HTTP GET請求一個JavaScript可讀會話cookie。在隨後的非GET請求中,服務器可以驗證該Cookie是否與X-XSRF-TOKEN HTTP標頭匹配,因此請確保只有在您的域中運行的JavaScript可以讀取令牌。令牌對於每個用戶必須是唯一的,並且必須由服務器驗證(以防止JavaScript構成自己的令牌)。我們建議該令牌是您的網站身份驗證Cookie的摘要,以增加安全性。

我找不到任何有關ASP.NET WebAPI的很好的例子,所以我在各種來源的幫助下推出了自己的例子。我的問題是 - 任何人都可以看到代碼有問題嗎?

首先我定義了一個簡單的輔助類:

public class CsrfTokenHelper 
{ 
    const string ConstantSalt = "<ARandomString>"; 

    public string GenerateCsrfTokenFromAuthToken(string authToken) 
    { 
     return GenerateCookieFriendlyHash(authToken); 
    } 

    public bool DoesCsrfTokenMatchAuthToken(string csrfToken, string authToken) 
    { 
     return csrfToken == GenerateCookieFriendlyHash(authToken); 
    } 

    private static string GenerateCookieFriendlyHash(string authToken) 
    { 
     using (var sha = SHA256.Create()) 
     { 
      var computedHash = sha.ComputeHash(Encoding.Unicode.GetBytes(authToken + ConstantSalt)); 
      var cookieFriendlyHash = HttpServerUtility.UrlTokenEncode(computedHash); 
      return cookieFriendlyHash; 
     } 
    } 
} 

然後我在我的授權控制器下面的方法,我稱之爲我叫FormsAuthentication.SetAuthCookie(後):

// http://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-(csrf)-attacks 
    // http://docs.angularjs.org/api/ng.$http 
    private void SetCsrfCookie() 
    { 
     var authCookie = HttpContext.Current.Response.Cookies.Get(".ASPXAUTH"); 
     Debug.Assert(authCookie != null, "authCookie != null"); 
     var csrfToken = new CsrfTokenHelper().GenerateCsrfTokenFromAuthToken(authCookie.Value); 
     var csrfCookie = new HttpCookie("XSRF-TOKEN", csrfToken) {HttpOnly = false}; 
     HttpContext.Current.Response.Cookies.Add(csrfCookie); 
    } 

然後我有一個自定義屬性,我可以將其添加到控制器以使其檢查csrf標頭:

public class CheckCsrfHeaderAttribute : AuthorizeAttribute 
{ 
    // http://stackoverflow.com/questions/11725988/problems-implementing-validatingantiforgerytoken-attribute-for-web-api-with-mvc 
    protected override bool IsAuthorized(HttpActionContext context) 
    { 
     // get auth token from cookie 
     var authCookie = HttpContext.Current.Request.Cookies[".ASPXAUTH"]; 
     if (authCookie == null) return false; 
     var authToken = authCookie.Value; 

     // get csrf token from header 
     var csrfToken = context.Request.Headers.GetValues("X-XSRF-TOKEN").FirstOrDefault(); 
     if (String.IsNullOrEmpty(csrfToken)) return false; 

     // Verify that csrf token was generated from auth token 
     // Since the csrf token should have gone out as a cookie, only our site should have been able to get it (via javascript) and return it in a header. 
     // This proves that our site made the request. 
     return new CsrfTokenHelper().DoesCsrfTokenMatchAuthToken(csrfToken, authToken); 
    } 
} 

最後,我清除CSRF令牌當用戶註銷:

HttpContext.Current.Response.Cookies.Remove("XSRF-TOKEN"); 

任何人都可以發現這種做法任何明顯的(或不那麼明顯)的問題?

+1

我試圖想出一個解決方案,以及想知道如果比較這兩個餅乾是好的,當他們都可以被攻擊者改變?如果你的鹽被發現,那麼這是不是妥協? – BenCr 2013-10-25 11:03:16

+0

BenCr,只有在我的域上運行的JavaScript才能讀取cookie並將其放入標題中。因此,如果存在導致瀏覽器向我的網站提交請求的惡意網站,請求將不會有頭,因此它會拒絕該請求。 – dbruning 2013-10-28 05:11:08

+0

你能解釋一下你在這裏描述的解決方案的結果嗎?它如何失敗?或者你是否要求我們在安全方面找到漏洞? – user1852503 2013-11-12 01:02:58

回答

-6

沒有任何問題與代碼指出,所以我認爲這個問題的答案。

6

你的代碼似乎很好。唯一的問題是,當web.api運行在asp.net mvc的頂層時,你不需要大部分代碼,而後者已經構建了對防僞令牌的支持。

在評論中,dbrunning和ccorrin表達了擔心,只有當您使用MVC html助手時,才能夠使用AntiForgery標記進行構建。這不是真的。助手可以公開基於會話的一對令牌,您可以互相驗證。詳情請參閱下文。

UPDATE:

有兩種方法可以從防僞使用:

  • AntiForgery.GetTokens使用兩個輸出參數返回cookie的令牌,並形成令牌

  • AntiForgery.Validate(cookieToken, formToken)驗證,如果對令牌有效

您完全可以重新調整這兩種方法的用途,並將formToken用作headerToken和cookieToken作爲實際的cookieToken。然後在屬性中調用validate。

另一種解決方案是使用智威湯遜(檢查如MembershipReboot實現)

This link展示瞭如何使用內置的防僞標記使用Ajax:

<script> 
    @functions{ 
     public string TokenHeaderValue() 
     { 
      string cookieToken, formToken; 
      AntiForgery.GetTokens(null, out cookieToken, out formToken); 
      return cookieToken + ":" + formToken;     
     } 
    } 

    $.ajax("api/values", { 
     type: "post", 
     contentType: "application/json", 
     data: { }, // JSON data goes here 
     dataType: "json", 
     headers: { 
      'RequestVerificationToken': '@TokenHeaderValue()' 
     } 
    }); 
</script> 


void ValidateRequestHeader(HttpRequestMessage request) 
{ 
    string cookieToken = ""; 
    string formToken = ""; 

    IEnumerable<string> tokenHeaders; 
    if (request.Headers.TryGetValues("RequestVerificationToken", out tokenHeaders)) 
    { 
     string[] tokens = tokenHeaders.First().Split(':'); 
     if (tokens.Length == 2) 
     { 
      cookieToken = tokens[0].Trim(); 
      formToken = tokens[1].Trim(); 
     } 
    } 
    AntiForgery.Validate(cookieToken, formToken); 
} 

也來看看這個問題AngularJS can't find XSRF-TOKEN cookie

+11

asp.net mvc中的防僞支持依賴於使用mvc生成您的html,以便它可以將請求驗證令牌放入您的HTML表單中作爲隱藏字段。我沒有使用mvc,因此我的html表單沒有這個標記。 – dbruning 2013-05-10 07:12:25

+0

@dbruning它只是幫手生成令牌,你可以在任何你想要的地方使用它 – vittore 2013-05-10 13:00:24

+1

也許。我不記得確切的細節,但我找不到一個乾淨的方式來請求csrf cookie。內置的AntiForgery方法似乎想要使用表單,而我只是使用POST'ed JSON數據。如果您可以共享一個乾淨的方式來獲取csrf cookie,那可能會取代上面的CsrfTokenHelper類。 您仍然需要一個很好的方法來設置傳出請求上的Cookie並檢查傳入請求上的標頭。 – dbruning 2013-05-13 07:00:55

0

我覺得你的代碼有缺陷。圍繞阻止CSRF的整個想法是阻止每個REQUEST上的唯一令牌,而不是每個會話。如果防僞標記是會話持久值,則執行CSRF的能力仍然存在。您需要爲每個請求提供唯一的令牌...

+2

[OWASP說](https://www.owasp.org/index.php/Anti_CSRF_Tokens_ASP.NET#ASP.NET_MVC_and_Web_API:_Anti-CSRF_Token)它是每個會話的標準做法 – dbruning 2016-06-16 01:52:09

0

此解決方案不安全,因爲只要Auth cookie有效,CSRF攻擊仍可能。當攻擊者通過另一個站點執行請求時,auth和xsrf cookie都會發送到服務器,因此在用戶進行「硬」註銷之前,您仍然處於脆弱狀態。

每個請求或會話都應該有自己的唯一標記來真正防止CRSF攻擊。但可能最好的解決方案是不使用基於cookie的身份驗證,而是使用基於令牌的身份驗證,如OAuth。這可以防止其他網站使用您的Cookie執行不需要的請求,因爲這些令牌用於http標頭而不是cookie。並且http頭不會自動發送。

  1. Token Based Authentication using ASP.NET Web API 2, Owin, and Identity
  2. AngularJS Token Authentication using ASP.NET Web API 2, Owin, and Identity

這些優秀的博客帖子中包含如何實現OAuth認證的WebAPI的信息。博客文章還包含有關如何將其與AngularJS集成的很好的信息。

另一種解決方案可能是禁用CORS並僅接受來自列入白名單的域的傳入請求。但是,這不適用於非網站應用程序,例如移動和/或桌面客戶端。除此之外,一旦您的網站容易受到XSS攻擊,攻擊者仍然可以在您的行爲中僞造請求。

+3

這是不正確的。惡意網站無法讓瀏覽器設置X-XSRF-TOKEN *標題*。 – dbruning 2016-06-16 01:54:20

+0

這似乎是Angular CookieXSRFStrategy的工作原理:https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#Protecting_REST_Services:_Use_of_Custom_Request_Headers。我打算將這個策略用於REST API。 – 2017-06-16 21:16:34