.net Core MVC:不接受X-SRF-TOKEN,400返回

Nat*_*lus 6 cookies csrf angularjs asp.net-core

我有一个使用angularJS的.net核心应用程序,我想保护受基于cookie的身份验证保护的api调用.我按照本文中的步骤操作:

https://docs.microsoft.com/en-us/aspnet/core/security/anti-request-forgery

  • 我添加了services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");我的服务配置
  • XSRF-TOKEN在加载页面时在开发人员工具中看到了cookie.
  • 我看到X-XSRF-TOKEN标题被添加到我的$ http发送请求中.

  • 我已将[AutoValidateAntiforgeryToken]我的控制器添加到处理ajax请求的控制器中.

  • 我已启用ssl,并通过https访问这些页面.

我可以按预期通过此api端点使GET请求正常.但是,我收到400错误,没有任何有关PUT和POST请求错误的详细信息.

我知道这X-XSRF-TOKEN是请求,(在chrome dev工具的网络选项卡中看到)所以我不确定我缺少什么来允许正确接收这些请求.

TL; DR:为什么.net核心拒绝我的有效AntiforgeryToken?

更新尝试@ joey建议的解决方案,但它没有工作,仍然收到400个响应.下面的代码反映了我试图解决这个问题的另一个解决方案(aka angularjs的解决方案,为跨站点脚本保护设置默认cookie和标头)

我还尝试配置AngularJS来改变cookie和标题名称与我在我的配置中匹配的名称Startup.cs.我更改了他们的名字以尝试XSRF-TOKEN(cookie)和X-XSRF-TOKEN(标题)以及,CSRF-TOKEN并且X-CSRF-TOKEN,虽然angular中的配置正确使用我提供的新默认值,但.net核心中的身份验证代码仍然无效.

有关更多信息,请参阅我如何配置AngularJS:

app.config(function ($httpProvider) {
    $httpProvider.defaults.xsrfHeaderName = 'X-CSRF-TOKEN';
    $httpProvider.defaults.xsrfCookieName = 'CSRF-TOKEN';
});
Run Code Online (Sandbox Code Playgroud)

这是ConfigureServicesStartup.cs文件中的行:

services.AddAntiforgery(options => options.HeaderName = "X-CSRF-TOKEN");
Run Code Online (Sandbox Code Playgroud)

最后这里是我添加到文件Configure方法的Startup.cs代码:

    app.Use(next => context => 
    {
        string path = context.Request.Path.Value;
        if (path.Contains("/MyProtectedPath/"))
        {
            var tokens = antiforgery.GetAndStoreTokens(context);
            context.Response.Cookies.Append("CSRF-TOKEN", tokens.RequestToken,
                new CookieOptions { HttpOnly = false });
        }
        return next(context);
    });
Run Code Online (Sandbox Code Playgroud)

更新2: 我已将以下内容添加到控制器操作中:

if (HttpContext.Request.Method.ToLower(CultureInfo.InvariantCulture) != "get")
{
    await _antiforgery.ValidateRequestAsync(HttpContext);
}
Run Code Online (Sandbox Code Playgroud)

AntiforgeryValidationException: The provided antiforgery token was meant for a different claims-based user than the current user.

我想也许antiforgery.GetAndStoreTokens(context)可能会覆盖页面加载时发送的当前cookie,所以我只在GET请求上点击该代码,但我得到了相同的帖子结果.

更新3 感谢@joey的帮助,我查看了我的startup.cs文件,并在单独的分支中使用了UseAuthentication:

    app.Use(next => context => //note: order of these use statements is key. keep this antiforgery section above auth!
    {
        string path = context.Request.Path.Value;
        if (path.ToLower(CultureInfo.InvariantCulture).Contains("/campaigns/proxy/"))
        {
            var tokens = antiforgery.GetAndStoreTokens(context);
            context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken,
                new CookieOptions { HttpOnly = false });
        }
        return next(context);
    });

    // Use Basic Auth for /api
    app.UseWhen(context => context.Request.Path.StartsWithSegments(new PathString("/api")), branch =>
    {
        branch.UseBasicAuth();
    });
    // Use Identity for everything except /api
    app.UseWhen(context => !context.Request.Path.StartsWithSegments(new PathString("/api")), branch =>
    {

        app.UseAuthentication(); // required for .net core 2.0 authentication
    });
Run Code Online (Sandbox Code Playgroud)

我不确定为什么我的令牌在这一点上会被视为无效......

Joe*_*oey 2

在您发布的代码中,您要添加 header X-XSRF-TOKEN,但 header 应该是X-CSRF-TOKEN.

您链接的网页中的示例提供了示例:

services.AddAntiforgery(options => options.HeaderName = "X-CSRF-TOKEN"); 
Run Code Online (Sandbox Code Playgroud)

更新:

感谢您通过附加代码和信息进行澄清。这里选择的实现 CSRF 的方法是将令牌作为初始 HTML 文件响应的标头进行传递。以下是如何为 SPA Web 应用程序配置此类情况的示例:

app.Use(next => context =>
{
    string path = context.Request.Path.Value;
    if (
        string.Equals(path, "/", StringComparison.OrdinalIgnoreCase) || 
        string.Equals(path, "/index.html", StringComparison.OrdinalIgnoreCase)
    )
    {
        var tokens = antiforgery.GetAndStoreTokens(context);
        context.Response.Cookies.Append("CSRF-TOKEN", tokens.RequestToken, 
            new CookieOptions() { HttpOnly = false });
    }

    return next(context);
});
Run Code Online (Sandbox Code Playgroud)

但是,如果您的服务配置为使用 String.Contains 方法 [1],antiforgery.GetAndStoreTokens(context)则会为包含任何位置的任何路径调用/MyProtectedPath/。这意味着它的配置方式不仅匹配/MyProtectedPath/,还/MyProtectectedPath/a/b/c匹配 或/a/b/c/MyProtectedPath/

要检查适用 HTTP 方法的后续请求中发送的 CSRF 令牌,如下所示:

if (string.Equals("POST", context.Request.Method, StringComparison.OrdinalIgnoreCase))
{
    await antiforgery.ValidateRequestAsync(context);
    // The line above will throw if the CSRF token is invalid.
}
Run Code Online (Sandbox Code Playgroud)

如果在此之前针对任何匹配路径调用该方法GetAndStoreTokens,则令牌将在检查之前被覆盖,这就是为什么 .net 示例通常GetAndStoreTokens首先排序,但具有查看路径和 HTTP 方法的特定条件。

[1] https://msdn.microsoft.com/en-us/library/dy85x1sa(v=vs.110).aspx