Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET Core MVC anti forgery

Trying to turn on anti forgery in core mvc project but with no luck. What was done:

Filter added to automatically check anti forgery token on every POST request.

services.AddMvc(o =>
{
  o.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
});

Token generation was added to each page this way.

@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Antiforgery;
@{
   var antiforgeryRequestToken = Antiforgery.GetAndStoreTokens(Context).RequestToken;
}
...
...
<script>
    var antiforgeryToken = @Json.Serialize(antiforgeryRequestToken);
</script>

And finally each client ajax request adds RequestVerificationToken this way.

var options = {
        url: o.url, type: 'POST', data: o.params, headers: { 'RequestVerificationToken': antiforgeryToken } };

I can see each ajax request has the token but I am always getting 400 for any POST request. If I disable the filter, it works fine. But once I enable it, asp.net core starts verification on each POST request and it always returns me 400.

Any ideas?

UPDATE:

I've followed the instructions I got in the comments and now the code looks like following. ConfigureServices method:

services.AddMvc(o => { 
  o.Filters.Add(new HandleAllExceptionsFilterFactory()); 
  o.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()); 
}); 

services.AddAntiforgery(o => o.CookieName = "XSRF-TOKEN");

And here is the middleware registered:

app.Use(next => context => { 
if (context.Request.Path == "/") 
{ 
    var antiforgery = app.ApplicationServices.GetService<IAntiforgery>(); 
    var token = antiforgery.GetAndStoreTokens(context); 
    context.Response.Cookies.Append("XSRF-TOKEN", token.RequestToken, new CookieOptions {HttpOnly = false}); 
} 

return next(context); 
});

I also removed any client side javascript code that has been sending the header before. But it still doesn't work.

like image 243
Andrei Tarutin Avatar asked Dec 18 '22 05:12

Andrei Tarutin


1 Answers

You're close, it's much simpler than you think it is.

First off, when you use services.AddMvc(); anti-forgery is already added. There's no need to add any filters, so remove those.

Then you'll want to change your anti-forgery configuration.

// Old
services.AddAntiforgery(o => o.CookieName = "XSRF-TOKEN"); 
// New
services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");

This is only required if you are using ajax. By default the anti-forgery system will only consider form data.

Next, in your views you can easily generate an anti-forgery token by using @Html.AntiForgeryToken(). There's no need for all that extra code. You can get rid of it. This will create a hidden <input /> with the value of the token. If your using a <form /> this will automatically be created for you if you use tag helpers such as asp-action and asp-controller. If you don't use those tag helpers you can use then use asp-antiforgery="true".

Now your ajax request can consume the hidden <input /> value. Here's an example:

$.ajax({
    method: "POST",
    url: "/api/test",
    data: data,
    beforeSend: function (xhr) {
        xhr.setRequestHeader("XSRF-TOKEN",
            $('input:hidden[name="__RequestVerificationToken"]').val());
    },
    statusCode: {
        200: function () {
            alert("Success");
        },
        400: function () {
            alert("Client error");
        },
        500: function () {
            alert("Server error");
        }
    }
});

The important part is the beforeSend function. This is where you set the request header to the same header name in you setup in Startup.cs

Now all you need to do is add [ValidateAntiForgeryToken] to the method you want.

Finally before you test be sure to remove the middleware you added.

This is only one way to do it. There are many other solutions found at the official documentation here.

like image 73
Travis Boatman Avatar answered Jan 07 '23 12:01

Travis Boatman