【问题标题】:ASP.NET Core MVC anti forgeryASP.NET Core MVC 防伪
【发布时间】:2017-12-28 11:33:27
【问题描述】:

尝试在核心 mvc 项目中打开防伪,但没有成功。做了什么:

添加过滤器以在每个 POST 请求上自动检查防伪令牌。

services.AddMvc(o =>
{
  o.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
});

以这种方式将令牌生成添加到每个页面。

@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Antiforgery;
@{
   var antiforgeryRequestToken = Antiforgery.GetAndStoreTokens(Context).RequestToken;
}
...
...
<script>
    var antiforgeryToken = @Json.Serialize(antiforgeryRequestToken);
</script>

最后每个客户端ajax请求都以这种方式添加RequestVerificationToken

var options = {
        url: o.url, type: 'POST', data: o.params, headers: { 'RequestVerificationToken': antiforgeryToken } };

我可以看到每个 ajax 请求都有令牌,但对于任何 POST 请求,我总是得到 400。如果我禁用过滤器,它工作正常。但是一旦我启用它,asp.net core 就会开始对每个 POST 请求进行验证,它总是返回 400。

有什么想法吗?

更新:

我已按照我在 cmets 中获得的说明进行操作,现在代码如下所示。 ConfigureServices 方法:

services.AddMvc(o => { 
  o.Filters.Add(new HandleAllExceptionsFilterFactory()); 
  o.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()); 
}); 

services.AddAntiforgery(o => o.CookieName = "XSRF-TOKEN");

这里是注册的中间件:

app.Use(next => context => { 
if (context.Request.Path == "/") 
{ 
    var antiforgery = app.ApplicationServices.GetService<IAntiforgery>(); 
    var token = antiforgery.GetAndStoreTokens(context); 
    context.Response.Cookies.Append("XSRF-TOKEN", token.RequestToken, new CookieOptions {HttpOnly = false}); 
} 

return next(context); 
});

我还删除了之前发送标头的所有客户端 JavaScript 代码。但是还是不行。

【问题讨论】:

  • 应该是data: { __RequestVerificationToken: antiforgeryToken }
  • 你的意思是标题名称必须从RequestVerificationToken 改为__RequestVerificationToken
  • 不仅,令牌应该在data。试试看是否可行。
  • 我还需要更改标题名称吗?
  • 是的,改成__RequestVerificationToken

标签: asp.net asp.net-mvc asp.net-core antiforgerytoken


【解决方案1】:

你已经接近了,它比你想象的要简单得多。

首先,当您使用services.AddMvc(); 时,已经添加了防伪功能。无需添加任何过滤器,因此请删除它们。

然后您需要更改防伪配置。

// Old
services.AddAntiforgery(o => o.CookieName = "XSRF-TOKEN"); 
// New
services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");

仅当您使用 ajax 时才需要。默认情况下,防伪系统只会考虑表单数据。

接下来,在您的视图中,您可以使用@Html.AntiForgeryToken() 轻松生成防伪令牌。不需要所有额外的代码。你可以摆脱它。这将使用令牌的值创建一个隐藏的&lt;input /&gt;。如果您使用&lt;form /&gt;,如果您使用asp-actionasp-controller 等标签助手,则会自动为您创建。如果您不使用那些可以使用的标签助手,请使用asp-antiforgery="true"

现在您的 ajax 请求可以使用隐藏的 &lt;input /&gt; 值。这是一个例子:

$.ajax({
    method: "POST",
    url: "/api/test",
    data: data,
    beforeSend: function (xhr) {
        xhr.setRequestHeader("XSRF-TOKEN",
            $('input:hidden[name="__RequestVerificationToken"]').val());
    },
    statusCode: {
        200: function () {
            alert("Success");
        },
        400: function () {
            alert("Client error");
        },
        500: function () {
            alert("Server error");
        }
    }
});

重要的部分是beforeSend 函数。这是您在 Startup.cs 中将请求标头设置为相同标头名称的地方

现在您需要做的就是将[ValidateAntiForgeryToken] 添加到您想要的方法中。

最后,在你测试之前一定要删除你添加的中间件。

这只是一种方法。官方文档here还有很多其他的解决方案。

【讨论】:

  • 感谢您的详细回复。关键是我在共享视图中的标记中生成了令牌。我们有 SPA 应用程序,并且共享视图在用户登录后没有被重新加载。这就是为什么我需要手动重新生成它。使用您的方法,用户登录之前会在输入字段中生成旧令牌值。
  • 哦,这是一个 SPA 应用程序?事先知道会很高兴。不过不用担心。你在使用 Angular、React、Vue 等吗?
  • 是的,这是SPA。
  • 解决方案效果很好,我对 .net core 2.0 所做的唯一更改是将标头设置为“X-XSRF-TOKEN”而不是“XSRF-TOKEN”
  • @JamesBlake 为什么需要更改标题名称?
【解决方案2】:

确保两个 cookie 具有相同的路径:

您可以在防伪配置选项中更改此设置。如果您使用 fetch 进行调用,请确保您发送的 cookie 包括凭据标头。

【讨论】:

    猜你喜欢
    • 2018-12-17
    • 2022-08-16
    • 1970-01-01
    • 1970-01-01
    • 2018-10-30
    • 2014-04-26
    • 2018-01-02
    • 2012-06-29
    • 2019-06-09
    相关资源
    最近更新 更多