ASP.NET Core React SPA 应用程序中的 ValidateAntiForgeryToken

Dav*_*vid 7 csrf asp.net-core

我正在尝试使用框架的工具向 ASP.NET Core React SPA 添加一些简单的 CSRF 验证。应用程序本身本质上是一个 create-react-app 设置(一个带有根元素的 index.html,其他所有内容都从捆绑的 JavaScript 加载)。

修补在链接上找到的一些信息,例如this one,我在我的 中设置了以下内容Startup.ConfigureServices:

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

并在我的 Chrome 工具中确认正在设置 cookie。如果我省略了上面的行,cookie 仍然设置了一个部分随机的名称,例如:.AspNetCore.Antiforgery.RAtR0X9F8_w cookie 正在设置中。我还确认,每当我重新启动整个应用程序时,cookie 值都会更新,因此框架会主动设置此 cookie。

在我的 Chrome 工具中观察网络请求,我确认 cookie 正在通过 AJAX 请求发送到服务器。在服务器上放置断点并观察Request.Cookies控制器操作中的值也证实了这一点。

但是,如果我装饰任何此类 AJAX 请求的操作,[ValidateAntiForgeryToken]则响应始终为空 400。

是否有我在某处错过的配置步骤?也许 action 属性找错了地方,我需要使用不同的验证?

itm*_*nus 9

我只是检查日志,发现有一个例外:

Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException:所需的防伪 cookie“.AspNetCore.Antiforgery.HPE6W9qucDc”不存在。在 Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgery.ValidateRequestAsync(HttpContext httpContext) 在 Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.ValidateAntiforgeryTokenAuthorizationFilter.OnAuthorizationAsync(AuthorizationFilterContext context)

这表示您忘记配置 cookie 名称:

   public void ConfigureServices(IServiceCollection services)
   {
       //services.AddAntiforgery();
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

       // In production, the React files will be served from this directory
       services.AddSpaStaticFiles(configuration =>
       {
           configuration.RootPath = "ClientApp/build";
       });
   }
Run Code Online (Sandbox Code Playgroud)

所以我只是添加一个配置如下:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAntiforgery(o => {
            o.Cookie.Name = "X-CSRF-TOKEN";
        });
        // ...
    }
Run Code Online (Sandbox Code Playgroud)

现在可以使用了。

此外,如果您想省略 的行services.AddAntiforgery(options => options.Cookie.Name = "X-CSRF-TOKEN");,您可以使用内置antiforgery.GetAndStoreTokens(context)方法发送 cookie:

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

两者都应该按预期工作。


joh*_*rom 7

当它建议通过 JS 可读 cookie 发送两个 cookie 时,这里接受的答案是极其不正确的:

// do not do this
context.Response.Cookies.Append("X-CSRF-TOKEN", tokens.CookieToken, new CookieOptions { HttpOnly = false });
context.Response.Cookies.Append("X-CSRF-FORM-TOKEN", tokens.RequestToken, new CookieOptions { HttpOnly = false });
Run Code Online (Sandbox Code Playgroud)

如果您在 JS 可读的 Cookie 中同时发送 Cookie 令牌和请求令牌,那么您就违背了拥有 Cookie 令牌和请求令牌的目的。

使用这两个令牌的目的是确保

  • 你有一个有效的会话(HTTP-only Cookie 证明了这一点),
  • 您已使用此有效会话向网站请求了一份表格(HTTP 可读 Cookie 或其他方法可以证明这一点),并且
  • 您正在从同一个有效会话提交表单

为什么这是错误的。

请求令牌

请求令牌可确保您实际加载了页面 (example.com/example-page)。想一想:如果您以管理员身份登录 example.com,则来自浏览器(其中 CORS 允许必要属性)的任何请求都可以成功验证基于 Cookie 的 CSRF 验证和您的身份验证。

但是,通过添加请求令牌,您可以确认您的浏览器在提交请求之前实际上也加载了对表单(或至少是网站)的请求。这通常是通过隐藏输入来完成的。这是通过使用 Asp.Net 中的表单标记帮助程序自动完成的。

<form action="/myEndpoint" method="POST">
  <input name="__RequestVerificationToken" type="hidden" value="@antiforgery.GetAndStoreTokens(context).RequestToken" />
  <button type="submit">Submit</button>
</form>
Run Code Online (Sandbox Code Playgroud)

它也可以设置在任何地方。,window.CSRFRequestToken并手动添加到 POST 请求,如本fetch例所示:

fetch('/myEndpoint', { method: 'POST', headers: { 'X-XSRF-Token': window.myCSRFRequestToken, 'Bearer': window.mySuperSecretBearerToken } };
Run Code Online (Sandbox Code Playgroud)

Cookie 令牌

在上面的示例中,用户通过 OAuth 或其他方式通过不记名令牌登录(不推荐,在浏览器环境中使用仅 HTTP Cookie)。

Cookie 令牌可确保恶意脚本无法泄露您的请求令牌并代表您发送请求。如果没有它,在供应链攻击中,恶意用户可以将您的秘密发送给恶意行为者:

window.addEventListener('load', => sendMySuperSecretInfoToTheShadowRealm(window.CSRFRequestToken, window.mySuperSecretBearerToken));
Run Code Online (Sandbox Code Playgroud)

现在,恶意用户可以使用您的 CSRF 和不记名令牌进行身份验证,从他们想要的任何地方发送请求。但!如果您有好朋友基于 HTTP-only Cookie 的 CSRF 验证,则不会——因为 JavaScript 无法读取 HTTP-only cookie。

解决方案

Asp.Net 通过设置 Cookie 令牌和请求令牌来组合这些解决方案。因此,当您向 AspNet 发送请求时,您同时发送:

饼干:

Cookies.Append('X-CSRF-Token', @antiforgery.GetAndStoreTokens(context).CookieToken);
Run Code Online (Sandbox Code Playgroud)

以及 aspnet 表单帮助器标记:

<form action="myEndpoint" />
Run Code Online (Sandbox Code Playgroud)

或手动打印令牌:

<form action="myEndpoint" asp-antiforgery="false">
    @Html.AntiForgeryToken()
</form>
Run Code Online (Sandbox Code Playgroud)

或手动向您的脚本提供令牌:

window.myCSRFRequestToken = "@antiforgery.GetAndStoreTokens(context).RequestToken)";
fetch('/myEndpoint', { method: 'POST', headers: { 'X-CSRF-Token': window.myCSRFRequestToken };
Run Code Online (Sandbox Code Playgroud)

别相信我的话

如果我没有解释清楚,请完整阅读此页:

https://learn.microsoft.com/en-us/aspnet/core/security/anti-request-forgery?view=aspnetcore-6.0

最后一点:

在上面的文档中,最后一个示例使用 cookie 来发送请求 cookie。这与这里的答案有着微妙的不同。接受的答案将两个 cookie 作为 Javascript-可读 发送{ HttpOnly = false }。这意味着 JavaScript 可以读取两者,恶意用户可以读取两者并自行制作特殊请求,该请求将根据 Cookie 和请求 CSRF 验证(在 CORS 允许的情况下)进行验证。

文档中,一个是通过 HTTP only cookie 发送的(JS 无法读取,仅用于基于 Cookie 的 CSRF 验证),另一个是通过 HTTP 可读的 cookie 发送。此 HTTP 可读 cookie 必须由 JavaScript 读取并与上述方法之一(表单输入、标头)一起使用,以便验证 CSRF 请求令牌验证。