首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Angular针对Asp.Net WebApi,在服务器上实现CSRF

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

Stack Overflow用户
提问于 2013-03-22 23:47:30
回答 4查看 16K关注 0票数 71

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

Angular.js有一些内置的功能,可以帮助进行抗csrf保护。对于每个http请求,它将查找名为"XSRF-TOKEN“的cookie,并将其作为名为"X-XSRF-TOKEN”的标头提交。

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

Angular documentation声明:

要利用这一点,服务器需要在第一个HTTP GET请求时在名为XSRF-TOKEN的JavaScript可读会话cookie中设置一个令牌。对于后续的非GET请求,服务器可以验证cookie是否与X-XSRF-TOKEN HTTP头匹配,因此请确保只有在您的域上运行的JavaScript才能读取该令牌。令牌对于每个用户都必须是唯一的,并且必须可由服务器验证(以防止JavaScript组成自己的令牌)。我们建议令牌是您站点的身份验证cookie的摘要,并使用salt以提高安全性。

我没有为ASP.NET WebAPI找到任何好的例子,所以我在各种来源的帮助下推出了我自己的例子。我的问题是--有人能看到代码有什么问题吗?

首先,我定义了一个简单的助手类:

代码语言:javascript
复制
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()之后调用它:

代码语言:javascript
复制
    // 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头:

代码语言:javascript
复制
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令牌:

代码语言:javascript
复制
HttpContext.Current.Response.Cookies.Remove("XSRF-TOKEN");

有没有人能发现这种方法中任何明显(或不那么明显)的问题?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2015-01-07 12:53:26

在代码中没有发现任何问题,所以我认为这个问题已经回答了。

票数 -5
EN

Stack Overflow用户

发布于 2013-05-10 09:16:06

你的代码看起来没问题。唯一的问题是,你不需要你拥有的大多数代码,因为web.api运行在asp.net mvc的“顶部”,而后者内置了对防伪令牌的支持。

在评论中,dbrunning和ccorrin表达了这样的担忧:只有在使用MVC html helper时,才能使用build In AntiForgery令牌。这不是真的。帮助器可以只公开基于会话的令牌对,您可以相互验证它们。有关详细信息,请参见下文。

更新:

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

  • AntiForgery.GetTokens使用两个out参数返回cookie令牌,表单token
  • AntiForgery.Validate(cookieToken, formToken)验证令牌对是否有效

您完全可以重用这两个方法,并将formToken用作headerToken,将cookieToken用作实际的cookieToken。然后只需在属性内对这两个函数调用validate。

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

This link展示了如何在ajax中使用内置防伪令牌:

代码语言:javascript
复制
<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

票数 8
EN

Stack Overflow用户

发布于 2016-04-07 10:40:28

我认为你的代码有缺陷。关于防止CSRF的整个想法是防止每个请求上的唯一令牌,而不是每个会话。如果防伪令牌是会话持久值,则仍然可以执行CSRF。您需要为每个请求提供一个唯一的令牌...

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15574486

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档