api.sample.com 背后有一个API,基于浏览器的webapp(单页面,Javascript驱动)背后有 app.sample.com 以及其他非基于浏览器的客户端 .

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> -submissions对攻击是开放的 .

为了保护表单,我 AllowAnonymousAttribute 设置了 AllowAnonymousAttribute .

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

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

[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令牌,因此保护取决于无法根据same-origin-policies读取javascript中的响应 .

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