[英]ASP.NET Core WebAPI: Validation of the provided antiforgery token failed. The cookie token and the request token were swapped
[英]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 Core 2 的源代碼。我找到了這個文件,尤其是第 145 行:
該行收到消息“防偽 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/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 丟失,我懷疑您的網站確實會在每個請求中發送它。 什么時候可以失蹤? 當它來自其他地方時。
關於另一個問題:
Configure
方法的身份驗證部分之后,將 AntiforgeryToken 作為 cookie 添加到響應中的中間位置是?在第一次 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.