[Blazor][Antiforgery] Error 400 for a static SSR page, related to the constant antiforgery token update, if the redirect was from another domain.
This issue has been moved from a ticket on Developer Community.
[severity:It bothers me. A fix would be nice] Problem: [Error 400][A valid antiforgery token was not provided with the request. Add an antiforgery token, or disable antiforgery validation for this endpoint] for a static SSR page, related to the constant antiforgery token update, if the redirect was from another domain.
I created a new default project BWA i-auto (i(nteractive)-server & i-wasm) + "include sample pages" in the latest VS 17.11.4 . I am attaching the project BlazorApp2.7z, it is a default + one simple test page (/test) with an edit form only.
If you open the /test page in a new tab and click submit - then the page works; Then if you open another /weather (sr-s-SSR=streaming rendering static SSR) page in a new tab from another domain - the previous page immediately gives 400 when submitting the form. (demonstrated in the video bandicam 2024-10-06 13-32-03-490.mp4) The problem is that the antiforgery token is updated if the redirect was to a new tab from another domain (demonstrated in the video bandicam 2024-10-06 13-48-18-096.mp4). The server IIS logs contain a new AF-token only for the last POST request to which the server responded with a 400 error. If you repeat this situation with opening the /weather page in a new tab with manual input of the url - then there is no such problem.
Is this a bug or some kind of Blazor technology issue for static pages? Can MS solve it?
The sample is temporarily deployed blazorapp220241005182634.azurewebsites.net
Possible workaround 1 for a s-SSR page: So far I see this workround: Use a js function that will check that the AF-token has not changed after loading the page and pressing the submit button. If it has changed, refresh the page (the AF-token will be updated automatically) or first ask the user to confirm that the page should be refreshed. But af-token cannot be read from cookies by design as "Httponly cookies' purpose is being inaccessible by script."
The question: How to find out with JavaScript that the AF-token has changed (a boolean sign is enough)?
Possible workaround 2 for a s-SSR page: When pages (like /weather) load, they detect that the token has changed and write the current timestamp to the browser's local storage. The test page, when a button is pressed, will detect that the state has changed (compare the timestamps via JS).
The question: How to find out with C# (OnInitializedAsync) that the AF-token has changed (a boolean sign is enough)?
Original Comments
Feedback Bot on 10/7/2024, 08:21 PM:
We have directed your feedback to the appropriate engineering team for further evaluation. The team will review the feedback and notify you about the next steps.
If you add the following to your Program.cs, does that fix your issue?
builder.Services.AddAntiforgery(options =>
{
options.Cookie.SameSite = SameSiteMode.None;
});
It appears that when the browser navigates to the /weather endpoint from another domain, antiforgery tries to reset the cookie because it wasn't sent during the initial request to the /weather endpoint. This invalidates the anti-csrf token used previously to render the /test form which was based on the previous cookie that got reset.
I don't think using SameSiteMode.None for the antiforgery cookie is a problem because an attacker trying to execute a cross-site request forgery attack should still be unable to generate a valid anti-csrf token. Maybe we should change the default.
However, I also see no reason Blazor should be triggering antiforgery logic at all when hitting the /weather endpoint. Not only is it inefficient, but it can also lead to problems like this. I know we're concerned about a form being rendered at a later point interactively, but maybe we could lazily make a delayed fetch request to get a valid cookie only once a form is rendered for components that support interactivity.
@blowdart @GrabYourPitchforks @javiercn Do any of you have opinions about using SameSiteMode.None for the antiforgery cookie? What about making that the default?
However, I also see no reason Blazor should be triggering antiforgery logic at all when hitting the /weather endpoint. Not only is it inefficient, but it can also lead to problems like this. I know we're concerned about a form being rendered at a later point interactively, but maybe we could lazily make a delayed fetch request to get a valid cookie only once a form is rendered for components that support interactivity.
We have a bug for this, I'd rather fix this behavior than changing the cookie
If you add the following to your Program.cs, does that fix your issue?
builder.Services.AddAntiforgery(options => { options.Cookie.SameSite = SameSiteMode.None; });
Thank you! It works for this use case, but it breaks another: if you open from another domain "/test" page in a new tab, the test page returns a 400 when submitting the form. (Without these edits, this use case works)
,
Related to https://github.com/dotnet/aspnetcore/issues/51981