Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Anti-Forgery Token Validation in MVC app with Blazor Server-side Component

With ASP.NET Core 3.0 out, I am trying to leverage server-side Blazor capabilities in my MVC Core app. I've started by creating a simple navigation component, from which I am trying to let users sign out with a button that points at a controller action. However, I am running into an Anti-Forgery Token Validation error that reads:

Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker: Information: Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.ViewFeatures.Filters.ValidateAntiforgeryTokenAuthorizationFilter'.

And then the server returns an HTTP 400 status code.

I'm following the Blazor App template code (Blazor version 0.7.0 with Visual Studio 2019):

<AuthorizeView>
    <Authorized>
        <a href="/Account/Manage">Hello, @context.User.Identity.Name!</a>
        <form method="post" action="/Account/Logout">
            <button type="submit">Sign out</button>
        </form>
    </Authorized>
    <NotAuthorized>
        <a href="/Account/Register">Register</a>
        <a href="/Account/Login">Log in</a>
    </NotAuthorized>
</AuthorizeView>

My MVC Account Controller has a Logout action annotated with [ValidateAntiForgeryToken], like this:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout()
{
    await _signInManager.SignOutAsync();
    _logger.LogInformation("User logged out.");
    return RedirectToAction(nameof(HomeController.Index), "Home");
}

In the past, I would add a @Html.AntiForgeryToken() in a form to conform with my annotated Logout action. In newer versions of ASP.NET Core you no longer needed to add this manually, as it is included within form tag helpers. But neither approach seems to translate or be available in a Blazor component.

How would you call this MVC action from a Blazor component and provide the Anti-forgery token for the controller validation? Or is this validation not needed at all anymore?

Even if my sign out case is not a big threat, this would be the same for calling any other action annotated similarly.

I might be missing a technical concept of CSRF that applies to the scenario of mixing MVC and Blazor.

like image 718
Giancarlo Sierra Avatar asked Dec 01 '19 01:12

Giancarlo Sierra


People also ask

What is anti-forgery token in an MVC application?

If you are developer, either fresher or experienced, you definitely have a little knowledge of Anti-Forgery Token in an MVC application. This is a built-in functionality provided by Microsoft. Developers often use it in their application. Everyone knows that this functionality is used for security purposes to stop attacks from hackers.

How to disable anti-forgery token validation in Razor pages?

Remember the following: 1 Anti-forgery token validation is enabled by default in Razor Pages. 2 You can disable validation either globally or on individual pages by using [IgnoreAntiforgeryToken]. 3 You can prevent forms from creating anti-forgery tokens by using asp-antiforgery="false" in the form tag helper. More items...

How do I send an antiforgery token to the server?

In traditional HTML-based apps, antiforgery tokens are passed to the server using hidden form fields. In modern JavaScript-based apps and SPAs, many requests are made programmatically. These AJAX requests may use other techniques (such as request headers or cookies) to send the token.

How secure is Blazor server JS Interop?

Because JS interop occurs over the Internet and the client uses a remote browser, Blazor Server apps share most web app security concerns. This topic describes common threats to Blazor Server apps and provides threat mitigation guidance focused on Internet-facing apps.


2 Answers

I have been trying to get anti-forgery to work for a Logout POST. I think I just managed to get it working. I'm open to suggestions for improvements!

I used an MS doc on Blazor Server additional security scenarios which explains a method for storing OIDC access and refresh tokens for use in Blazor components. I modified that example in order to use an anti-forgery token in a similar way as follows:

  1. I created a TokenProvider class to store the anti-forgery token.
public class TokenProvider
{
    public string AntiforgeryToken { get; set; }
}
  1. Added a Scoped TokenProvider to DI in Startup.ConfigureServices()
public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddScoped<TokenProvider>();
    ...
}
  1. Got the anti-forgery token in _Host.cshtml and passed it as a parameter to my App component
@page "/"
@namespace My.Pages
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery antiforgery
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

@{
    var token = antiforgery.GetAndStoreTokens(HttpContext).RequestToken;
}
...
<body>
  <component type="typeof(App)" render-mode="ServerPrerendered" param-AntiforgeryToken="token" />
...
  1. App.razor then populates the TokenProvider during OnInitializedAsync()
@inject TokenProvider tokenProvider

<Router ....
</Router>

@code {
    [Parameter]
    public string AntiforgeryToken { get; set; }

    protected override Task OnInitializedAsync()
    {
        tokenProvider.AntiforgeryToken = AntiforgeryToken;
        return base.OnInitializedAsync();
    }
}
  1. TokenProvider can then be consumed via injection in my Logout.razor Component
@inject TokenProvider tokenProvider
...
<form method="post" action="Logout">
    <input name="__RequestVerificationToken" type="hidden" value="@tokenProvider.AntiforgeryToken">
    <button type="submit" class="nav-link btn btn-link" title="Log out">
        <span class="oi oi-account-logout"></span>
    </button>
</form>
...
like image 120
Rob Avatar answered Sep 27 '22 21:09

Rob


Not sure why this Exception popped up. This worked for me though. Just add this to the end of your _Host.cshtml file.

   @Html.AntiForgeryToken()
</body>
like image 31
Marcel Gelijk Avatar answered Sep 27 '22 23:09

Marcel Gelijk