javascript - Angular 针对 Asp.Net WebApi,在服务器上实现 CSRF

标签 javascript asp.net angularjs asp.net-web-api csrf

我正在使用 Angular.js 实现一个网站,它正在访问 ASP.NET WebAPI 后端。

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

这依赖于网络服务器能够在对用户进行身份验证后设置 XSRF-TOKEN cookie,然后检查传入请求的 X-XSRF-TOKEN header 。

Angular documentation状态:

To take advantage of this, your server needs to set a token in a JavaScript readable session cookie called XSRF-TOKEN on first HTTP GET request. On subsequent non-GET requests the server can verify that the cookie matches X-XSRF-TOKEN HTTP header, and therefore be sure that only JavaScript running on your domain could have read the token. The token must be unique for each user and must be verifiable by the server (to prevent the JavaScript making up its own tokens). We recommend that the token is a digest of your site's authentication cookie with salt for added security.

我无法为 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;
        }
    }
}

然后我的授权 Controller 中有以下方法,我在调用 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);
    }

然后我有一个自定义属性,我可以将其添加到 Controller 以使其检查 csrf header :

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 token :

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

谁能发现这种方法有任何明显(或不太明显)的问题?

最佳答案

您的代码似乎没问题。唯一的问题是,您不需要大部分代码,因为 web.api 在 asp.net mvc 的“顶部”运行,后者内置了对防伪 token 的支持。

在评论中,dbrunning 和 ccorrin 表达了对只有在使用 MVC html 帮助程序时才能使用内置 AntiForgery token 的担忧。这不是真的。 Helpers 可以公开基于 session 的 token 对,您可以相互验证这些 token 。详情请见下文。

更新:

您可以使用 AntiForgery 中的两种方法:

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

  • AntiForgery.Validate(cookieToken, formToken) 验证 token 对是否有效

您完全可以重新调整这两个方法的用途,并将 formToken 用作 headerToken,将 cookieToken 用作实际的 cookieToken。然后只需在两个属性内调用验证。

另一种解决方案是使用 JWT(检查例如 MembershipReboot 实现)

This link显示如何使用带有 ajax 的内置防伪 token :

<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

关于javascript - Angular 针对 Asp.Net WebApi,在服务器上实现 CSRF,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15574486/

相关文章:

javascript - js函数中的'语法错误,意外标记='

javascript - 通过 ng-include 重用之前实例化的模板

javascript - 在 Windows 7 64 位中更新 node.js

javascript - 如何居中对齐 TemplateField 的标题文本?

javascript - 使用 Observables 时处理服务器错误

javascript - AngularJS 从 Controller 外部的 AJAX 获取返回值

javascript - 从 jquery 调用 Controller 函数

javascript - 嵌套 json 对象中数组过滤器的使用

asp.net - 访问远程服务器 IIS7 上的虚拟目录时出现运行时错误

asp.net - 为什么 ASP.Net MVC 将我的 "Fixed"区域重命名为 "_Fixed"