簡體   English   中英

顯然是隨機錯誤:“防偽令牌驗證失敗。防偽 cookie 令牌和請求令牌不匹配。”

[英]Apparently Random Error: "Antiforgery token validation failed. The antiforgery cookie token and request token do not match."

背景

我有一個相對較新的 ASP.NET Core 2 站點。 它只在一台服務器上運行(Windows Server 2012 R2,IIS 8.5),我每隔幾天在上傳更新時才重啟一次網站。 大約每天一次,用戶的請求由於被防偽系統拒絕而失敗。 這些是 POST 請求,並沒有什么特別之處。 我在 POST 請求中包含了防偽值,並且在 99% 的情況下,POST 請求都有效。 但是當他們不這樣做時,stdout 日志會說,“防偽令牌驗證失敗。防偽 cookie 令牌和請求令牌不匹配。” 當我使用該確切語句執行 Web 搜索時,我得到零結果。 所以我求助於 Stack Overflow。 [這不再正確,因為 Web 搜索現在會產生這個 Stack Overflow 問題。]

錯誤

我在下面包含了標准輸出日志的相關部分。

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.'.

對於導致上述標准輸出 output 的請求, 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.RequestToken ,所以這可能與它有關。

ASP.NET GitHub 上的核心 2

我決定看看 ASP.NET Core 2 的源代碼。我找到了這個文件,尤其是第 145 行:

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

該行收到消息“防偽 cookie 令牌和請求令牌不匹配。” 從第 134 行的文件中:

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

所以我認為這就是消息的來源,但我仍然想知道為什么會這樣。

問題

有人可以幫我弄清楚為什么這些防偽令牌無法驗證嗎? 用戶的 Web 瀏覽器是否有可能破壞 cookie 或 POST 數據? 有沒有人在這方面有經驗或有任何建議? 謝謝你。

全局禁用過濾器似乎是關閉它的唯一方法。 我得到了@svallis 的代碼來進行視覺修改:

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

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

我在這里找到了解決方案: 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>();

關於 CookieToken 為空:也許該請求是偽造的? 由於 cookie 丟失,我懷疑您的網站確實會在每個請求中發送它。 什么時候可以失蹤? 當它來自其他地方時。

關於另一個問題:

  1. Configure方法的身份驗證部分之后,將 AntiforgeryToken 作為 cookie 添加到響應中的中間位置是?
  2. 您是否有可能在多個 GET 請求中返回多個 AntiforgeryToken? 在這種情況下,可能存在競爭條件,該請求返回最新的(瀏覽器將使用什么)並且哪個請求最后離開服務器; 這些可能不同 -> 令牌不匹配。

在第一次 Get 操作時,您應該只返回一個。 當您在一個實例上托管前端和后端時,它可能是根 (/),如果您在另一個端口上使用 API,則應確保在 POST、PUT 或 PATCH 之前執行 GET。

一實例啟動示例:

builder.Use(next => context =>
{
    var path = context.Request.Path.Value;
    if (!string.Equals(path, "/", StringComparison.OrdinalIgnoreCase) ||
        context.Request.Method != "GET") return next(context);
    var tokens = antiforgery.GetAndStoreTokens(context);
    context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken,
        new CookieOptions { HttpOnly = false, Path = "/" });

    return next(context);
});

多端口實例(注意 CORS)

public IServiceProvider ConfigureServices(IServiceCollection services)
{
  services.AddCors(options =>
  {
     options.AddDefaultPolicy(builder => builder
            .WithOrigins(/*allowed domains here*/)
            .AllowAnyHeader()
            .WithMethods("GET", "POST", "PUT", "DELETE")
            .AllowCredentials()
            .Build()
     );
  });
}
public virtual void Configure(IApplicationBuilder app, IAntiforgery antiforgery)
{
  app.UseAuthentication()
     .UseAuthorization()
     .Use(next => context =>
      {
         var path = context.Request.Path.Value;
         if (!string.Equals(path, "/api/settings", StringComparison.OrdinalIgnoreCase) ||
             context.Request.Method != "GET") return next(context);
         var tokens = antiforgery.GetAndStoreTokens(context);
         context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken,
             new CookieOptions { HttpOnly = false });

         return next(context);
      });
}

請記住,如果您在另一個端口/位置托管前端,則應為 withCredentials 添加標題,如下所示:

return this.http.get<Settings>(`/api/settings`, { withCredentials: true });

我一直在尋找新的 .NET 6.0 Blazor 服務器應用程序的解決方案,該應用程序具有身份驗證和類似 MSDN 的修復功能,但沒有禁用防偽造功能:

 app.UseRouting();

app.UseAuthorization();

var antiforgery = app.Services.GetRequiredService<IAntiforgery>();

app.Use((context, next) =>
{
    var requestPath = context.Request.Path.Value;

    if (string.Equals(requestPath, "/", StringComparison.OrdinalIgnoreCase)
        || string.Equals(requestPath, "/index.html", StringComparison.OrdinalIgnoreCase))
    {
        var tokenSet = antiforgery.GetAndStoreTokens(context);
        context.Response.Cookies.Append("XSRF-TOKEN", tokenSet.RequestToken!,
            new CookieOptions { HttpOnly = false });
    }

    return next(context);
});

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM