.NET 11 introduces a Fetch Metadata-based automatic CSRF protection mechanism built directly into ASP.NET Core pipelines. Replacing traditional cryptographic synchronizer tokens (__RequestVerificationToken) and heavy server-side Data Protection validation, the new CsrfProtectionMiddleware automatically validates browser-managed headers (Sec-Fetch-Site, Sec-Fetch-Mode, and Origin) across all Blazor project templates.
| Architectural Aspect | Traditional Token Model (.NET 8/9/10) | .NET 11 Automatic CSRF |
|---|---|---|
| Validation Mechanism | Cryptographic token matching in form payload/cookies | Browser-managed Fetch Metadata header evaluation |
| State & Cryptography | Requires Data Protection keys, server state, per-request encryption | Zero cryptographic overhead (pure header evaluation) |
| Multi-Tab Reliability | Prone to token race conditions and invalidations across tabs | Seamless execution across multiple tabs |
| Middleware Pipeline | Required explicit app.UseAntiforgery() and component attributes | Enabled automatically via WebApplication.CreateBuilder |
| Non-Browser Callers | Required token suppression or custom validation rules | Automatically allowed (missing Fetch Metadata headers) |
The CsrfProtectionMiddleware evaluates incoming requests early in the pipeline using a deterministic, 5-step decision hierarchy:
GET, HEAD, OPTIONS, and TRACE are permitted unconditionally (RFC 9110 §9.2.1).Sec-Fetch-Site is same-origin or none (user typed URL, clicked bookmark, or navigated same-site) are allowed.Origin header matching the endpoint’s configured CORS policy are allowed.Sec-Fetch-Site and Origin headers (e.g., cURL, Postman, mobile native SDKs, server-to-server callers) pass through, as CSRF is strictly a browser-based attack vector.POST, PUT, DELETE, PATCH missing explicit CORS approval) is marked invalid and rejected with an HTTP 400 Bad Request.<form action="..." method="post"> submissions within static components no longer require hidden token fields or @attribute [RequireAntiforgeryToken].IAntiforgeryValidationFeature deferred state. Same-origin form posts succeed automatically./blazor/negotiate) is evaluated by the CSRF middleware. Because it originates from the app’s same origin, it passes automatically.HttpClient calls send Sec-Fetch-Site: same-origin and require zero special configuration.https://app.example.com and communicates with https://api.example.com: POST/PUT/DELETE calls will be blocked by default.Sec-Fetch-Site: none or omit headers entirely depending on the OS platform.Third-party providers (e.g., Stripe webhooks, Entra ID / OAuth form_post logins) send cross-origin POST requests to your app without CORS headers. These specific endpoints must opt out of CSRF protection:
app.MapPost("/api/webhooks/stripe", () => Results.Ok())
.DisableAntiforgery();
@attribute [IgnoreAntiforgeryToken]
To permit cross-origin requests from trusted frontends without disabling CSRF globally:
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
policy.WithOrigins("https://my-blazor-wasm-app.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
HttpClient Only Tests for Security Automation: Automated integration tests using raw HttpClient do not emit browser Sec-Fetch-Site headers and will pass through Rule 4. Test browser authentication flows using Playwright or Selenium.localhost:5001 and the API on localhost:7001, configure launch settings or reverse proxies to avoid cross-origin dev breakage.DisableCsrfProtection in app configuration as a temporary diagnostic step to isolate whether CSRF middleware is the cause.