Skip to content

Fix antiforgery/CSRF middleware lost on primary path when using re-execution#67667

Closed
oroztocil wants to merge 1 commit into
mainfrom
oroztocil/fix-csrf-reroute-antiforgery
Closed

Fix antiforgery/CSRF middleware lost on primary path when using re-execution#67667
oroztocil wants to merge 1 commit into
mainfrom
oroztocil/fix-csrf-reroute-antiforgery

Conversation

@oroztocil

Copy link
Copy Markdown
Member

Summary

Fixes #67628. A newly created Blazor Web App (dotnet new blazor + dotnet run) returns HTTP 500 on every page on 11.0 Preview 7 (regression from Preview 6), failing with Endpoint / (/) contains anti-forgery metadata, but a middleware was not found that supports anti-forgery. This also turned the Templates.Mvc.Test.BlazorTemplateTest CI leg red on essentially every run.

Analysis

The Blazor Web template no longer calls app.UseAntiforgery() explicitly (removed in #67119); it relies on the framework auto-injecting the antiforgery/CSRF middleware. WebApplicationBuilder stores that implicit post-routing middleware (authentication/authorization/CSRF) in a single-use application-property slot, __Internal_PostRoutingPipeline, which EndpointRoutingMiddleware consumes (and removes) so it runs right after routing and stamps the marker that EndpointMiddleware checks for.

The template also calls app.UseStatusCodePagesWithReExecute(...). In the WebApplication case this goes through RerouteHelper.Reroute, which builds a re-execution branch that itself calls UseRouting(). Because the user pipeline (containing the re-execution middleware) is composed before the primary routing middleware, that branch's EndpointRoutingMiddleware consumes and removes the single-use slot from the shared global route builder first. The primary request pipeline's routing is then built without the block, so a normal request to an antiforgery-required endpoint (the Blazor root /) finds no antiforgery/CSRF marker and EndpointMiddleware throws.

The bug affects all RerouteHelper.Reroute callers: UseStatusCodePagesWithReExecute, UseExceptionHandler, UsePathBase, and UseRewriter. Existing tests only exercised the re-executed path (which received the stolen block), so the normal-request regression went uncovered.

Fix

Capture the __Internal_PostRoutingPipeline slot from the shared global route builder before building the re-execution branch and restore it afterwards, in RerouteHelper.Reroute (src/Shared/Reroute.cs). This lets the re-execution branch keep its own copy of the implicit middleware (preserving CSRF on re-executed paths) while leaving the slot in place for the primary pipeline's routing. The single-use semantics for the main pipeline are unchanged, so repeated UseRouting() calls still cannot double-apply the block.

Tests

Added three regression tests to CsrfProtectionIntegrationTests covering the previously-untested shape (a normal request to an antiforgery-required endpoint when re-execution is configured), for both implicit and explicit routing, plus a guard that cross-origin POSTs are still rejected.

Verified locally (all green):

  • CsrfProtectionIntegrationTests (61)
  • WebApplicationTests (299)
  • Diagnostics StatusCode/ExceptionHandler (54)
  • Http.Abstractions UsePathBase (52)
  • Rewrite (473)
  • Routing EndpointMiddleware/EndpointRoutingMiddleware (37)

Notes

Draft for review. The app.UseAntiforgery() workaround from the issue remains valid; this change removes the need for it.

The Blazor Web template relies on the framework auto-injecting the antiforgery/CSRF
middleware, which WebApplicationBuilder stores in a single-use post-routing slot
consumed by EndpointRoutingMiddleware. RerouteHelper.Reroute (used by
UseStatusCodePagesWithReExecute, UseExceptionHandler, UsePathBase and UseRewriter)
builds a re-execution branch whose routing consumes that slot from the shared global
route builder before the primary routing is built, stealing it from the primary
request pipeline. A normal request to an antiforgery-required endpoint then throws
'a middleware was not found that supports anti-forgery' (HTTP 500).

Capture and restore the post-routing slot around the re-execution branch build so
both the primary and re-executed pipelines run the implicit middleware.

Fixes #67628

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant