启用CORS的REST API浏览器客户端的CSRF保护

时间:2016-06-28 09:08:16

标签: javascript c# asp.net-web-api cors csrf

api.sample.com背后有一个API,app.sample.com背后有一个基于浏览器的网络应用程序(单页,Javascript驱动)以及其他非基于浏览器的客户端。

API支持基本和基于令牌的身份验证,并将令牌打包到浏览器的cookie中。

要使设置适用于浏览器,必须启用CORS并设置withCredentials:

var cors = new EnableCorsAttribute("https://app.sample.com", "*", "GET,POST,PUT,DELETE");
cors.SupportsCredentials = true;
cors.PreflightMaxAge = 3600;
config.EnableCors(cors);

现在我使用了CSRF漏洞并发现AJAX被原始设置阻止,<img><a>没有看到任何响应数据,但<form> - 提交已开启利用。

为了保护表单,我在混音中添加了一个防伪标记,并针对没有设置AllowAnonymousAttribute的控制器操作的每个请求验证它。

我想使用System.Web.Helpers.AntiForgery类,但它需要设置主体标识,而不是在登录时验证凭据时的情况 - 仅在后续请求中。

所以我想到了浏览器客户端在验证凭据后立即请求CSRF-Token的想法,在每个请求上将令牌作为标头发送,服务器在FilterAttribute中验证令牌。 E.g。

[HttpGet, Route("xsrf"), AllowAnonymous]
public IHttpActionResult GetCSRFToken()
{
    // AllowAnonymous is set to allow requests without csrf token to reach this action
    if (User.Identity.IsAuthenticated)
    {
        string cookieToken, formToken;
        AntiForgery.GetTokens(null, out cookieToken, out formToken);
        // or set a cookie
        return Ok(cookieToken + ":" + formToken);
    }
    return Unauthorized();
}

public class ValidateXSRFTokenAttribute : AuthorizationFilterAttribute
{
    public override void OnAuthorization(HttpActionContext actionContext)
    {
        // apply only if cookie-authentication and no AllowAnonymousAttribute set on the action
        if (actionContext.RequestContext.Principal.Identity.AuthenticationType == DefaultAuthenticationTypes.ApplicationCookie &&
            !actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any())
        {
            try
            {
                string cookieToken = "";
                string formToken = "";
                IEnumerable<string> tokenHeaders;
                if (actionContext.Request.Headers.TryGetValues("X-XSRF-Token", out tokenHeaders))
                {
                    var tokens = tokenHeaders.First().Split(':');
                    if (tokens.Length == 2)
                    {
                        cookieToken = tokens[0].Trim();
                        formToken = tokens[1].Trim();
                    }
                }
                AntiForgery.Validate(cookieToken, formToken);
            }
            catch
            {
                actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
                {
                    ReasonPhrase = "Anti-XSRF Validation Failed."
                };
            }
        }

        base.OnAuthorization(actionContext);
    }
}

此实施是否正确?有什么需要改进的吗? 仍然可以使服务器使用<form><iframe>提交来发送CSRF令牌,因此保护取决于无法根据同源策略读取javascript中的响应

或者更好地滚动我自己的CSRF-Token,可以在登录时直接设置?

0 个答案:

没有答案