对Asp.Net WebApi的angular度,在服务器上实现CSRF

我在Angular.js中实现了一个网站,这是一个ASP.NET WebAPI后端。

Angular.js有一些内置的function来帮助防止csrf保护。 在每个http请求中,它将查找名为“XSRF-TOKEN”的cookie并将其作为名为“X-XSRF-TOKEN”的头提交。

这依赖于Web服务器能够在authentication用户之后设置XSRF-TOKEN cookie,然后检查传入请求的X-XSRF-TOKEN标头。

Angular文档指出:

要利用这一点,您的服务器需要在第一个HTTP GET请求中的JavaScript可读会话cookie(称为XSRF-TOKEN)中设置一个令牌。 在随后的非GET请求中,服务器可以validationcookie是否与X-XSRF-TOKEN HTTP标头匹配,因此请确保只有在您的域上运行的JavaScript可以读取令牌。 令牌对于每个用户必须是唯一的,并且必须由服务器validation(以防止JavaScript构成自己的令牌)。 我们build议该令牌是您的网站的身份validationCookie摘要与盐,以增加安全性。

对于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"); 

任何人都可以发现任何明显的(或不是那么明显)的问题吗?

你的代码似乎很好。 唯一的事情是,你不需要大部分的代码,因为web.api运行在asp.net mvc的“顶层”,而后者已经构build了对防伪令牌的支持。

在注释中,dbrunning和ccorrinexpression了担心,只有在使用MVC html助手的情况下,才能使用AntiForgery标记进行构build。 这不是真的。 助手可以公开基于会话的一对令牌,您可以互相validation。 详情请参阅下文。

更新:

有两种方法可以从AntiForgery中使用:

  • AntiForgery.GetTokens使用两个输出参数来返回cookie标记和表单标记

  • AntiForgery.Validate(cookieToken, formToken)validation令牌对是否有效

你完全可以重新使用这两种方法,并将formToken作为headerToken和cookieToken用作实际的cookieToken。 然后在属性内部调用validate。

另一个解决scheme是使用JWT(检查例如MembershipReboot实现)

此链接显示如何使用内置的防伪标记与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找不到XSRF-TOKEN cookie

我认为你的代码是有缺陷的。 围绕防止CSRF的整个想法是阻止每个REQUEST上的唯一令牌,而不是每个会话。 如果防伪标记是会话持久值,则执行CSRF的能力仍然存在。 您需要为每个请求提供一个唯一的标记…

这个解决scheme是不安全的,因为只要Auth cookie有效,CSRF攻击仍然是可能的。 当攻击者通过其他站点执行请求时,auth和xsrf cookie都将被发送到服务器,因此在用户进行“硬”注销之前,您仍然处于脆弱状态。

每个请求或会话都应该有自己的唯一标记来真正防止CRSF攻击。 但可能最好的解决scheme是不使用基于cookie的身份validation,而是使用基于令牌的身份validation,如OAuth。 这可以防止其他网站使用您的Cookie来执行不需要的请求,因为这些令牌用在http标头而不是cookies中。 而http头不会自动发送。

  1. 基于令牌的身份validation使用ASP.NET Web API 2,Owin和Identity
  2. 使用ASP.NET Web API 2,Owin和Identity的AngularJS令牌身份validation

这些优秀的博客文章包含了如何为WebAPI实现OAuth的信息。 博客文章还包含了如何将其与AngularJS集成的很好的信息。

另一个解决scheme可能是禁用CORS,只接受来自列入白名单的域的传入请求。 但是,这不适用于非网站应用程序,如移动和/或桌面客户端。 接下来,一旦您的网站容易受到XSS攻击,攻击者仍然可以伪造您的行为请求。

没有任何问题与代码指出,所以我认为这个问题的答案。