Created | ![]() |
Favourites | Opened | Upvotes | Comments |
6. Nov 2019 | 1 | 0 | 364 | 0 | 0 |
CSRF or XSRF : Cross Site Request Forgery (also sometimes called Session Surfing)
So what is CSRF - the following process illustrates CSRF :
Note that while token based authorization is gaining popularity and the token based authorization method is not vulnerable to CSRF, cookie based authorization is still heavily in use and CSRF therefore a problem.
The most common way to protect against CSRF is the Syncronizer Token Pattern (STP).
In case of a traditional html form, 2 encrypted AntiForgery tokens are sent by the Authorization middleware (in addition to the Authorization cookie):
The field token looks like this : <input name="__RequestVerificationToken" type="hidden" value="CfDJ8NrAkSldwD9CpLRyOtm6FiJB1Jr_F3FQJQDvhlHoLNJJrLA6zaMUmhjMsisu2D2t-FkAiYgyWQawJk9vNm36sYP1esHOtamBEPvSk1_x--Sg8Ey2a-d9CV2zHVWIN9MVhvKHOSyKqdZFlYDVd69XYx-rOWPw3ilHGLN6K0Km-1p83jZzF0E4WU5OGg5ns2-m9Yw" />
Then posting the html form back to a server endpoint that is set to check for CSRF, the Authorization middleware will throw an HttpAntiForgeryException in any of the following cases :
The idea for the html form is that if badsite.com uses javascript to post to goodsite.com, the javascript in badsite.com CANNOT read the field token of the browser loaded goodsite.com and therefore not send a matching token to goodsite.com.
In ASP.NET Core you can enable CSRF protection either :
Open Startup.cs and navigate to the ConfigureServices section (where you add services to the Dependency Injection Container). Typically you will add the MVC service like services.AddMvc(...) and that is there you enable global CSRF :
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options => {
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
... other options ...
});
... other services ...
}
... other stuff ...
}
All controller actions will now automatically implement the Syncronizer Token Pattern, however you can disable it on particular actions (or controllers) if necessary using the [IgnoreValidateAntiForgeryToken] decorator :
public class HomeController : Controller
{
[IgnoreValidateAntiForgeryToken]
public JsonResult UploadImage(IFormFile file) {...}
... other actions ...
}
Enable CSRF protection globally is recommended, however it can also be done in a more adhoc way on the individual controllers or even individual actions.
In ASP.NET Core there are 3 decorations available for Controllers and for Actions :
Eg. if you have enabled CRSF validation globally in startup.cs or locally on current Controller, you can use [IgnoreValidateAntiForgeryToken] on a specific Action to cancel CRSF validation on that particular Action.
Adding CSRF protection on Controller level :
[ValidateAntiForgeryToken]
public class HomeController : Controller
{
public IActionResult Edit(EditModel model) {...} // automatically protected
[IgnoreValidateAntiForgeryToken] // don't validate CSRF tokens in this Action
public JsonResult UploadImage(IFormFile file) {...}
... any other Action is automatically protected ...
}
Adding CSRF protection on Action level :
public class HomeController : Controller
{
[ValidateAntiForgeryToken] // adhoc protect this Action
public IActionResult Edit(EditModel model) {...}
... any other Action is not protected (unless you decorate it) ...
}
In case of submitting an HTML form, the CSRF protection works automatically because the CSRF middleware adds the cookie token (to a cookie) and the field token to a hidden input field and then submitted, the CSRF middleware will extract both the cookie token and the field token and compare them.
However, if you post using AJAX (Asynchronous Javascript And Xml), then there is no automatic transfer of the field token and the http endpoint (Action) validation will fail. Instead then using AJAX, we need to transfer the token field in the request header.
While you may be able to extract the field token from the hidden input field that contains the field token, and add the field token to the AJAX request header, it is more reliant to request the field token directly from the CSRF middleware : Microsoft.AspNetCore.Antiforgery.
You can request the field token adhoc in the View that contains your AJAX Javascript and add it from there to your AJAX request headers.
Here is a View that request the field token and then add it to an AJAX request (here using jQuery to do the AJAX request as that is probably the most common method) :
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Csrf
@functions {
public string GetAntiCsrfRequestToken() // create a server-side function to return the field token
{
return Csrf.GetAndStoreTokens(Context).RequestToken;
}
}
<script>
$.ajax({
url: '@Url.Action("Register", "Account")',
type: 'POST',
data: JSON.stringify(registerData),
contentType: "application/json; charset=utf-8",
headers: {'RequestVerificationToken': '@GetAntiCsrfRequestToken()'}, // call the server-side GetAntiCsrfRequestToken function defined above to insert the field token server-side
dataType: 'json',
success: function (resultData) {
.. do something or not ..
}
error: function (xmlHttpRequest, textStatus, errorThrown) {
.. typically notify the user of failue ..
}
}
</script>
Note that the AJAX request header name is RequestVerificationToken (not prefixed with underscore like the hidden field name is).
Also note that typically you will have many AJAX calls spread over many different Views, in which case it is better to request the field token in your Layout View and add it to a global Javascript object (so you don't need to setup field token request in each View from where you do AJAX requests).