This quick post is a response to a question about anti-forgery tokens I saw on twitter:
I want to apply ValidateAntiForgeryToken to every action on all HttpPost requests in #AspNetCore MVC. Any easy way to do this? #dotnetcore
— Tugberk Ugurlu (@tourismgeek) 10 June 2017
Anti-forgery tokens are a security mechanism to defend against cross-site request forgery (CSRF) attacks. Marius Schulz shared a solution to this problem in a blog post in which he creates a simple middleware to automatically validate the tokens sent in the request. This works, but there's actually an even simpler solution I wanted to share: the built-in AutoValidateAntiforgeryTokenAttribute
.
tl;dr Add the
AutoValidateAntiforgeryTokenAttribute
as a global MVC filter to automatically validate all appropriate action methods.
Defending against cross-site request forgery in ASP.NET Core
I won't go into CSRF attacks in detail - I recommend you check out the docs for details if this is all new to you. In essence, when you send a form to the user, you add an extra hidden field that includes one half of a cryptographic token. Additionally, a cookie is set with the other half of the token. When the form is posted to the server, the two halves of the token are verified, ensuring only valid requests can be made.
Note, this post assumes you are using Razor and server-side rendering. You can use a similar approach to protect your API calls, but I won't go into that here.
In ASP.NET Core, the tokens are added to your forms automatically when you use the asp-*
tag helpers, for example:
<form asp-controller="Manage" asp-action="ChangePassword" method="post">
<!-- Form details -->
</form>
This will generate markup similar to the following. You can see the hidden input with the anti-forgery token:
<form method="post" action="/Manage/ChangePassword">
<!-- Form details -->
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8NrAkSldwD9CpLR...LongValueHere!" />
</form>
Note, in ASP.NET Core 2.0, ASP.NET Core will add anti-forgery tokens to all your forms, whether you have use the
asp-*
tag helpers or not.
Adding the form field is just one part of the requirement, you also need to actually check that the tokens are valid on the server side. You can do this by decorating your controller actions with the [ValidateAntiForgeryToken]
attribute. You'll need to add it to all of your POST actions to properly protect your application:
public class ManageController
{
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult ChangePassword()
{
// ...
return View();
}
}
This works, but is a bit of a pain - you have to decorate each of your POST action methods with the attribute. If you forget, you won't get an error, the action just won't be protected.
Automatically validating all appropriate actions
MVC has the concept of "Global filters", which are applied to every action in your MVC app. Unfortunately we can't just add the [ValidateAntiForgeryToken]
attribute globally. We won't receive anti-forgery tokens for certain types of requests like GET or HEAD - if we applied [ValidateAntiForgeryToken]
globally, then all of those requests would throw validation errors.
Luckily, ASP.NET Core provides another attribute for just such a use, the [AutoValidateAntiForgeryToken]
attribute. This works identically to the [ValidateAntiForgeryToken]
attribute, except that it ignores "safe" methods like GET and HEAD.
Adding the attribute to your application is simple - just add it to the global filters collection in your Startup
class, when calling AddMvc()
.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
});
}
}
Job done! No need to decorate all your action methods with attributes, you will just be protected automatically!
Useful links
- https://blog.mariusschulz.com/2017/06/11/global-antiforgery-token-validation-in-asp-net-core
- https://docs.microsoft.com/en-us/aspnet/core/security/anti-request-forgery
- https://en.wikipedia.org/wiki/Cross-site_request_forgery
- https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/AutoValidateAntiforgeryTokenAuthorizationFilter.cs