显然是随机错误:“防伪令牌验证失败。防伪cookie令牌和请求令牌不匹配。”

时间:2018-01-10 03:08:55

标签: c# iis asp.net-core csrf kestrel-http-server

背景

我有一个相对较新的ASP.NET Core 2站点。它只在一台服务器上运行(Windows Server 2012 R2,IIS 8.5),每上几天我上传更新时只会重启一次。大约每天一次,由于防伪系统的拒绝,用户的请求失败。这些是POST请求,并没有什么特别之处。我在POST请求中包含了防伪值,99%的时间,POST请求都有效。但是当它们没有时,stdout日志会说,“防伪令牌验证失败。防伪cookie令牌和请求令牌不匹配。”当我使用该确切语句执行Web搜索时,我得到零结果。所以我转向Stack Overflow。 [这已不再适用,因为Web搜索现在会产生此Stack Overflow问题。]

错误

我已在下面列出了stdout日志的相关部分。

info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 POST [domain redacted] application/x-www-form-urlencoded 234
info: Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.ValidateAntiforgeryTokenAuthorizationFilter[1]
      Antiforgery token validation failed. The antiforgery cookie token and request token do not match.
Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException: The antiforgery cookie token and request token do not match.
   at Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgery.ValidateTokens(HttpContext httpContext, AntiforgeryTokenSet antiforgeryTokenSet)
   at Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgery.<ValidateRequestAsync>d__9.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.ValidateAntiforgeryTokenAuthorizationFilter.<OnAuthorizationAsync>d__3.MoveNext()
info: Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker[3]
      Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.AutoValidateAntiforgeryTokenAuthorizationFilter'.
info: Microsoft.AspNetCore.Mvc.StatusCodeResult[1]
      Executing HttpStatusCodeResult, setting HTTP status code 400
info: Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker[2]
      Executed action /Index in 2.6224ms
warn: Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgery[1]
      Antiforgery validation failed with message 'The antiforgery cookie token and request token do not match.'.

对于导致上述stdout输出的请求,IAntiforgery.IsRequestValidAsync同意返回false。请注意错误消息“防伪cookie令牌和请求令牌不匹配”。这是一个失败的POST请求和相关cookie的缩减示例。

POST: __RequestVerificationToken= CfDJ8F9Fs4CqDFpLttT96eZw9WHjWfHO8Yawn35k4Yq3gDK5n1TDJDDiY5o86VQs1_qOVIYBydCizBU4knb7Jmq1-heGhwnMu2KmhUIiAd0xI7Sudv3GX-J0OI6wRfiPL4L1KRs2Pml8dbsDfwemewBqi18

Cookie: .AspNetCore.Antiforgery.ClRyCRmWApY=CfDJ8F9Fs4CqDFpLttT96eZw9WFtJht41WcNrmgshi2pFGwcxhr0_0hvINQc7Yl9Cbjhv-TiSNXeEctyKborLI49AcjHfWIgOmmKkbjOe7QMn8Z0WZtkQy5JcaBHKEGTu1p-La8JL8pZZqZy02Hrswpkh3I

在请求失败并出现400错误(使用一些错误处理中间件)后,我也已经捕获了几次这些数据:

AntiforgeryTokenSet tokens = antiforgery.GetTokens(context);
tokens.CookieToken:  null
tokens.FormFieldName:  "__RequestVerificationToken"
tokens.HeaderName:  "RequestVerificationToken"
tokens.RequestToken:  "CfDJ8F9Fs4CqDFpLttT96eZw9WH33jSw5mM8h7RpEd3vGISQTRkx1rfwm-L2lfkvXKMBc-riESmoTo_fnIjeBbRmOo5KuJHr09f8B75sQ9g_djIVeeaGwMw5KE6W1O2-7Vi03fCnwlTv8l-BWGst76Ln-ZQ"

所以这里有三个字符串:

POST String:  "CfDJ8F9Fs4CqDFpLttT96eZw9WHjWfHO8Yawn35k4Yq3gDK5n1TDJDDiY5o86VQs1_qOVIYBydCizBU4knb7Jmq1-heGhwnMu2KmhUIiAd0xI7Sudv3GX-J0OI6wRfiPL4L1KRs2Pml8dbsDfwemewBqi18"
Cookie String:  "CfDJ8F9Fs4CqDFpLttT96eZw9WFtJht41WcNrmgshi2pFGwcxhr0_0hvINQc7Yl9Cbjhv-TiSNXeEctyKborLI49AcjHfWIgOmmKkbjOe7QMn8Z0WZtkQy5JcaBHKEGTu1p-La8JL8pZZqZy02Hrswpkh3I"
antiforgery.GetTokens(context).RequestToken:  "CfDJ8F9Fs4CqDFpLttT96eZw9WH33jSw5mM8h7RpEd3vGISQTRkx1rfwm-L2lfkvXKMBc-riESmoTo_fnIjeBbRmOo5KuJHr09f8B75sQ9g_djIVeeaGwMw5KE6W1O2-7Vi03fCnwlTv8l-BWGst76Ln-ZQ"

POST字符串和cookie字符串不匹配,但根据我的经验,即使ASP.NET Core认为合法的请求,他们也从不这样做。但奇怪的是,POST字符串和tokens.RequestToken也不匹配。虽然我在请求生命周期的后期捕获了tokens.RequestTooken,但我认为它们应该匹配,所以也许这与它有关。

GitHub上的ASP.NET Core 2

我决定查看ASP.NET Core 2的源代码。我找到了这个文件,特别是第145行:

https://github.com/aspnet/Antiforgery/blob/dev/src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgeryTokenGenerator.cs

该行收到消息“防伪cookie令牌和请求令牌不匹配”。从第134行的这个文件:

https://github.com/aspnet/Antiforgery/blob/dev/src/Microsoft.AspNetCore.Antiforgery/Resources.resx

所以我认为这就是消息的来源,但我仍然想知道为什么会这样。

问题

请有人帮我弄清楚为什么这些防伪代币没有验证?用户的Web浏览器是否可能会破坏cookie或POST数据?有没有人有这方面的经验或任何建议?谢谢。

2 个答案:

答案 0 :(得分:3)

全局禁用过滤器似乎是关闭过滤器的唯一方法。我得到了@ svallis的代码来修改视线:

services.AddMvc().AddRazorPagesOptions(options =>
{
    options.Conventions.ConfigureFilter(new IgnoreAntiforgeryTokenAttribute());
});

https://github.com/aspnet/Mvc/issues/7012

答案 1 :(得分:0)

我在这里找到了解决方法:https://github.com/aspnet/Antiforgery/issues/116

using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;


// fix from https://github.com/aspnet/Antiforgery/issues/116
namespace WebAppCore.Code
{
    public class HtmlGeneratorNoStoreAntiforgery: DefaultHtmlGenerator  
    {
        public HtmlGeneratorNoStoreAntiforgery(
            IAntiforgery antiforgery,
            IOptions<MvcViewOptions> optionsAccessor,
            IModelMetadataProvider metadataProvider,
            IUrlHelperFactory urlHelperFactory,
            HtmlEncoder htmlEncoder,
            ValidationHtmlAttributeProvider validationAttributeProvider)
            : base(antiforgery, optionsAccessor, metadataProvider, urlHelperFactory, htmlEncoder, validationAttributeProvider)
        {
        }

        public override IHtmlContent GenerateAntiforgery(ViewContext viewContext)
        {
            var result = base.GenerateAntiforgery(viewContext);

            viewContext
                    .HttpContext
                    .Response
                    .Headers[HeaderNames.CacheControl]
                = "no-cache, max-age=0, must-revalidate, no-store";

            return result;
        }
    }
}

并添加startup.cs:

services.AddSingleton<Microsoft.AspNetCore.Mvc.ViewFeatures.IHtmlGenerator, HtmlGeneratorNoStoreAntiforgery>();